Ctrl K
ComunidadGuías…

src/lib/openapi-credentials.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * The API's OpenAPI document cut down to what one credential can call.
 *
 * Every operation carries `x-opendiving-credentials`, the credentials the API accepts on it,
 * derived there from the dependencies that enforce them. The document's own `security` cannot
 * say this: an access token and an API key share one bearer scheme.
 */

export const CREDENTIALS_EXTENSION = "x-opendiving-credentials";

export const CREDENTIAL_OPTIONS = [
  { value: "session", label: "Signed-in session" },
  { value: "superuser", label: "Superuser session" },
  { value: "api-key-read", label: "API key: read" },
  { value: "api-key-write", label: "API key: read & write" },
  { value: "anonymous", label: "No credential" },
] as const;

export type Credential = (typeof CREDENTIAL_OPTIONS)[number]["value"];

type OpenApiDocument = Record<string, unknown>;
type Operation = Record<string, unknown>;

const HTTP_METHODS = new Set([
  "get",
  "put",
  "post",
  "delete",
  "options",
  "head",
  "patch",
  "trace",
]);

function operationsOf(document: OpenApiDocument): Operation[] {
  const paths = (document.paths ?? {}) as Record<
    string,
    Record<string, unknown>
  >;
  return Object.values(paths).flatMap((item) =>
    Object.entries(item)
      .filter(([key]) => HTTP_METHODS.has(key))
      .map(([, operation]) => operation as Operation),
  );
}

/**
 * Whether the document names each operation's credentials. A build of the API from before the
 * extension does not, and the filter has nothing to filter on there.
 */
export function namesCredentials(document: OpenApiDocument): boolean {
  return operationsOf(document).some((operation) =>
    Array.isArray(operation[CREDENTIALS_EXTENSION]),
  );
}

/**
 * The document with only the operations `credential` may call. A path left with none is dropped,
 * and so is a top-level tag no remaining operation uses, so the sidebar has no empty groups.
 */
export function filterByCredential(
  document: OpenApiDocument,
  credential: Credential,
): OpenApiDocument {
  const paths = (document.paths ?? {}) as Record<
    string,
    Record<string, unknown>
  >;
  const kept: Record<string, Record<string, unknown>> = {};

  for (const [path, item] of Object.entries(paths)) {
    const filtered = Object.fromEntries(
      Object.entries(item).filter(([key, operation]) => {
        if (!HTTP_METHODS.has(key)) return true;
        const accepted = (operation as Operation)[CREDENTIALS_EXTENSION];
        return Array.isArray(accepted) && accepted.includes(credential);
      }),
    );
    if (Object.keys(filtered).some((key) => HTTP_METHODS.has(key))) {
      kept[path] = filtered;
    }
  }

  const result: OpenApiDocument = { ...document, paths: kept };
  if (Array.isArray(document.tags)) {
    const used = new Set(
      operationsOf(result).flatMap((operation) =>
        Array.isArray(operation.tags) ? (operation.tags as string[]) : [],
      ),
    );
    result.tags = (document.tags as { name: string }[]).filter((tag) =>
      used.has(tag.name),
    );
  }
  return result;
}