0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor: filter whole supplement document if needed (#6085)

This commit is contained in:
Gao Sun 2024-06-23 10:33:28 +08:00 committed by GitHub
parent 6dd487269d
commit 097dfcac89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 29 additions and 21 deletions

View file

@ -3,12 +3,12 @@
{
"name": "Organization applications",
"description": "Manage organization - application relationships. An application can be associated with one or more organizations in order to get access to the organization resources.\n\nCurrently, only machine-to-machine applications can be associated with organizations."
}
},
{ "name": "Dev feature" }
],
"paths": {
"/api/organizations/{id}/applications": {
"get": {
"tags": ["Dev feature"],
"summary": "Get organization applications",
"description": "Get applications associated with the organization.",
"responses": {
@ -18,7 +18,6 @@
}
},
"post": {
"tags": ["Dev feature"],
"summary": "Add organization application",
"description": "Add an application to the organization.",
"requestBody": {
@ -44,7 +43,6 @@
}
},
"put": {
"tags": ["Dev feature"],
"summary": "Replace organization applications",
"description": "Replace all applications associated with the organization with the given data.",
"requestBody": {
@ -72,7 +70,6 @@
},
"/api/organizations/{id}/applications/{applicationId}": {
"delete": {
"tags": ["Dev feature"],
"summary": "Remove organization application",
"description": "Remove an application from the organization.",
"responses": {
@ -84,7 +81,6 @@
},
"/api/organizations/{id}/applications/{applicationId}/roles": {
"get": {
"tags": ["Dev feature"],
"summary": "Get organization application roles",
"description": "Get roles associated with the application in the organization.",
"responses": {
@ -94,7 +90,6 @@
}
},
"post": {
"tags": ["Dev feature"],
"summary": "Add organization application role",
"description": "Add a role to the application in the organization.",
"requestBody": {
@ -120,7 +115,6 @@
}
},
"put": {
"tags": ["Dev feature"],
"summary": "Replace organization application roles",
"description": "Replace all roles associated with the application in the organization with the given data.",
"requestBody": {
@ -148,7 +142,6 @@
},
"/api/organizations/{id}/applications/{applicationId}/roles/{organizationRoleId}": {
"delete": {
"tags": ["Dev feature"],
"summary": "Remove organization application role",
"description": "Remove a role from the application in the organization.",
"responses": {

View file

@ -3,12 +3,12 @@
{
"name": "Security",
"description": "Security related endpoints."
}
},
{ "name": "Dev feature" }
],
"paths": {
"/api/security/subject-tokens": {
"post": {
"tags": ["Dev feature"],
"summary": "Create a new subject token.",
"description": "Create a new subject token for the use of impersonating the user.",
"requestBody": {

View file

@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { httpCodeToMessage } from '@logto/core-kit';
import { condString, conditionalArray, deduplicate } from '@silverhand/essentials';
import { condArray, condString, conditionalArray, deduplicate } from '@silverhand/essentials';
import deepmerge from 'deepmerge';
import { findUp } from 'find-up';
import type { IMiddleware } from 'koa-router';
@ -23,6 +23,7 @@ import type { AnonymousRouter } from '../types.js';
import {
buildTag,
devFeatureTag,
findSupplementFiles,
normalizePath,
removeUnnecessaryOperations,
@ -134,11 +135,13 @@ const identifiableEntityNames = Object.freeze([
]);
/** Additional tags that cannot be inferred from the path. */
const additionalTags = Object.freeze([
'Organization applications',
'Organization users',
'Security',
]);
const additionalTags = Object.freeze(
condArray<string>(
EnvSet.values.isDevFeaturesEnabled && 'Organization applications',
EnvSet.values.isDevFeaturesEnabled && 'Security',
'Organization users'
)
);
/**
* Attach the `/swagger.json` route which returns the generated OpenAPI document for the
@ -201,7 +204,7 @@ export default function swaggerRoutes<T extends AnonymousRouter, R extends Route
assertThat(routesDirectory, new Error('Cannot find routes directory.'));
const supplementPaths = await findSupplementFiles(routesDirectory);
const supplementDocuments = await Promise.all(
const allSupplementDocuments = await Promise.all(
supplementPaths.map(async (path) =>
removeUnnecessaryOperations(
// eslint-disable-next-line no-restricted-syntax -- trust the type here as we'll validate it later
@ -210,6 +213,13 @@ export default function swaggerRoutes<T extends AnonymousRouter, R extends Route
)
);
// Filter out supplement documents that are for dev features when dev features are disabled.
const supplementDocuments = allSupplementDocuments.filter(
(supplement) =>
EnvSet.values.isDevFeaturesEnabled ||
!supplement.tags?.find((tag) => tag?.name === devFeatureTag)
);
const baseDocument: OpenAPIV3.Document = {
openapi: '3.0.1',
servers: [

View file

@ -15,7 +15,9 @@ const capitalize = (value: string) => value.charAt(0).toUpperCase() + value.slic
/** The tag name used in the supplement document to indicate that the operation is cloud only. */
const cloudOnlyTag = 'Cloud only';
/** The tag name is used in the supplement document to indicate that the corresponding API operation is a dev feature. */
const devFeatureTag = 'Dev feature';
export const devFeatureTag = 'Dev feature';
const reservedTags = new Set([cloudOnlyTag, devFeatureTag]);
/**
* Get the root component name from the given absolute path.
@ -145,7 +147,7 @@ export const validateSupplement = (
const originalTags = new Set(original.tags?.map((tag) => tag.name));
for (const { name } of supplementTags) {
if (!originalTags.has(name)) {
if (!reservedTags.has(name) && !originalTags.has(name)) {
throw new TypeError(
`Supplement document contains extra tag \`${name}\`. If you want to add a new tag, please add it to the \`additionalTags\` array in the main swagger route file.`
);
@ -213,7 +215,10 @@ export const validateSwaggerDocument = (document: OpenAPIV3.Document) => {
}
for (const tag of document.tags ?? []) {
assert(tag.description, `Tag \`${tag.name}\` must have a description.`);
assert(
reservedTags.has(tag.name) || tag.description,
`Tag \`${tag.name}\` must have a description.`
);
}
}
};