diff --git a/packages/core/src/routes/organization/application/index.openapi.json b/packages/core/src/routes/organization/application/index.openapi.json index 2f75e22ad..5eeaa63ca 100644 --- a/packages/core/src/routes/organization/application/index.openapi.json +++ b/packages/core/src/routes/organization/application/index.openapi.json @@ -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": { diff --git a/packages/core/src/routes/security/index.openapi.json b/packages/core/src/routes/security/index.openapi.json index b575b14bc..bbee8d166 100644 --- a/packages/core/src/routes/security/index.openapi.json +++ b/packages/core/src/routes/security/index.openapi.json @@ -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": { diff --git a/packages/core/src/routes/swagger/index.ts b/packages/core/src/routes/swagger/index.ts index 19d8f743e..94bb7fc4a 100644 --- a/packages/core/src/routes/swagger/index.ts +++ b/packages/core/src/routes/swagger/index.ts @@ -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( + 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 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 + EnvSet.values.isDevFeaturesEnabled || + !supplement.tags?.find((tag) => tag?.name === devFeatureTag) + ); + const baseDocument: OpenAPIV3.Document = { openapi: '3.0.1', servers: [ diff --git a/packages/core/src/routes/swagger/utils/general.ts b/packages/core/src/routes/swagger/utils/general.ts index 9d112b62a..509172478 100644 --- a/packages/core/src/routes/swagger/utils/general.ts +++ b/packages/core/src/routes/swagger/utils/general.ts @@ -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.` + ); } } };