From 9bb9c010919d8313f02042839087ca2d9929be25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Mon, 1 Dec 2025 20:53:55 +0100 Subject: [PATCH 1/3] chore: spec generation to not include tables by default --- .changeset/gentle-trees-tan.md | 6 ++++++ .../generate/implementation/oas-spec.ts | 5 ++++- packages/cli/src/commands/generate/index.ts | 6 ++++++ .../oas/generate/methods/do-oas-update.ts | 12 ++++++++++-- .../oas/generate/methods/patch-oas-spec.ts | 17 +++++++++++------ .../generate/methods/update-spec-for-group.ts | 3 +++ .../core/src/implementations/generate-oas.ts | 2 ++ packages/core/src/index.ts | 12 ++++++++++-- 8 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 .changeset/gentle-trees-tan.md diff --git a/.changeset/gentle-trees-tan.md b/.changeset/gentle-trees-tan.md new file mode 100644 index 0000000..f693e38 --- /dev/null +++ b/.changeset/gentle-trees-tan.md @@ -0,0 +1,6 @@ +--- +"@calycode/core": minor +"@calycode/cli": minor +--- + +chore: by default do not generate table schemas into the OAS spec, but allow that via flag diff --git a/packages/cli/src/commands/generate/implementation/oas-spec.ts b/packages/cli/src/commands/generate/implementation/oas-spec.ts index 6ff1d67..1e8f8bc 100644 --- a/packages/cli/src/commands/generate/implementation/oas-spec.ts +++ b/packages/cli/src/commands/generate/implementation/oas-spec.ts @@ -15,6 +15,7 @@ async function updateOasWizard({ isAll = false, printOutput = false, core, + includeTables = false, }: { instance: string; workspace: string; @@ -23,6 +24,7 @@ async function updateOasWizard({ isAll: boolean; printOutput: boolean; core; + includeTables?: boolean; }) { attachCliEventHandlers('generate-oas', core, { instance, @@ -60,7 +62,8 @@ async function updateOasWizard({ workspaceConfig.name, branchConfig.label, groups, - startDir + startDir, + includeTables ); for (const { group, generatedItems } of allGroupResults) { const apiGroupNameNorm = normalizeApiGroupName(group); diff --git a/packages/cli/src/commands/generate/index.ts b/packages/cli/src/commands/generate/index.ts index d1cbb43..4f153c8 100644 --- a/packages/cli/src/commands/generate/index.ts +++ b/packages/cli/src/commands/generate/index.ts @@ -108,6 +108,11 @@ function registerGenerateCommands(program, core) { addApiGroupOptions(specGenCommand); addPrintOutputFlag(specGenCommand); + specGenCommand.option( + '--include-tables', + 'Requests table schema fetching and inclusion into the generate spec. By default tables are not included.' + ); + specGenCommand.action( withErrorHandler(async (opts) => { await updateOasWizard({ @@ -118,6 +123,7 @@ function registerGenerateCommands(program, core) { isAll: opts.all, printOutput: opts.printOutputDir, core: core, + includeTables: opts.includeTables, }); }) ); diff --git a/packages/core/src/features/oas/generate/methods/do-oas-update.ts b/packages/core/src/features/oas/generate/methods/do-oas-update.ts index e8610d6..4aeb38e 100644 --- a/packages/core/src/features/oas/generate/methods/do-oas-update.ts +++ b/packages/core/src/features/oas/generate/methods/do-oas-update.ts @@ -15,15 +15,23 @@ async function doOasUpdate({ inputOas, instanceConfig, workspaceConfig, - storage, // Used for meta lookups, not FS + storage, + includeTables = false, }: { inputOas: any; instanceConfig: any; workspaceConfig: any; storage: any; + includeTables: boolean; }): Promise { // Patch and enrich OAS - const oas = await patchOasSpec({ oas: inputOas, instanceConfig, workspaceConfig, storage }); + const oas = await patchOasSpec({ + oas: inputOas, + instanceConfig, + workspaceConfig, + storage, + includeTables, + }); // Prepare output artifacts (relative paths) const generatedItems: GeneratedItem[] = [ diff --git a/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts b/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts index da75997..c58081b 100644 --- a/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts +++ b/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts @@ -1,15 +1,21 @@ import { cleanupResponseSchemas } from './cleanup-response-schemas'; import { generateTableSchemas } from '..'; -async function patchOasSpec({ oas, instanceConfig, workspaceConfig, storage }) { - +async function patchOasSpec({ + oas, + instanceConfig, + workspaceConfig, + storage, + includeTables = false, +}) { const newOas = { ...oas }; - const tableSchemas = await generateTableSchemas({ instanceConfig, workspaceConfig, storage }); + const tableSchemas = includeTables + ? await generateTableSchemas({ instanceConfig, workspaceConfig, storage }) + : {}; newOas.openapi = '3.1.1'; newOas.components = { - ...(oas.components ?? {}), responses: { @@ -225,7 +231,6 @@ async function patchOasSpec({ oas, instanceConfig, workspaceConfig, storage }) { bearerFormat: 'JWT', }), }, - }; newOas.security = newOas.security || [{ bearerAuth: [] }]; @@ -235,4 +240,4 @@ async function patchOasSpec({ oas, instanceConfig, workspaceConfig, storage }) { return oasWithPatchedResponseSchemas; } -export { patchOasSpec } \ No newline at end of file +export { patchOasSpec }; diff --git a/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts b/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts index 912a5d3..fa54e12 100644 --- a/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts +++ b/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts @@ -12,6 +12,7 @@ async function updateSpecForGroup({ workspaceConfig, storage, core, + includeTables = false, }: { group: any; instanceConfig: any; @@ -19,6 +20,7 @@ async function updateSpecForGroup({ branchConfig: any; storage: any; core: any; + includeTables: boolean; }): Promise<{ oas: string; generatedItems: GeneratedItem[]; @@ -36,6 +38,7 @@ async function updateSpecForGroup({ instanceConfig, workspaceConfig, storage, + includeTables, }); // Optionally emit info for consumer diff --git a/packages/core/src/implementations/generate-oas.ts b/packages/core/src/implementations/generate-oas.ts index 0bcb8fc..61df9ca 100644 --- a/packages/core/src/implementations/generate-oas.ts +++ b/packages/core/src/implementations/generate-oas.ts @@ -10,6 +10,7 @@ async function updateOpenapiSpecImplementation( workspace: string; branch: string; groups: ApiGroup[]; + includeTables?: boolean; }, startDir: string ): Promise<{ group: string; oas: any; generatedItems: { path: string; content: string }[] }[]> { @@ -42,6 +43,7 @@ async function updateOpenapiSpecImplementation( branchConfig, storage, core, + includeTables: options.includeTables ?? false, }); return { group: grp.name, oas, generatedItems }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e038fa9..607c70f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -146,7 +146,8 @@ export class Caly extends TypedEmitter { workspace: string, branch: string, groups: any, - startDir: string + startDir: string, + includeTables?: boolean ): Promise<{ group: string; oas: any; generatedItems: { path: string; content: string }[] }[]> { return updateOpenapiSpecImplementation( this.storage, @@ -156,6 +157,7 @@ export class Caly extends TypedEmitter { workspace, branch, groups, + includeTables, }, startDir ); @@ -386,7 +388,12 @@ export class Caly extends TypedEmitter { * }); * ``` */ - async doOasUpdate({ inputOas, instanceConfig, workspaceConfig }): Promise<{ + async doOasUpdate({ + inputOas, + instanceConfig, + workspaceConfig, + includeTables = false, + }): Promise<{ oas: any; generatedItems: { path: string; @@ -398,6 +405,7 @@ export class Caly extends TypedEmitter { instanceConfig, workspaceConfig, storage: this.storage, + includeTables, }); } From db93d8508ff88a598ff7eb91ccef5c67e893211e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Mon, 1 Dec 2025 20:53:55 +0100 Subject: [PATCH 2/3] chore: spec generation to not include tables by default --- .changeset/gentle-trees-tan.md | 6 ++++++ .../generate/implementation/oas-spec.ts | 5 ++++- packages/cli/src/commands/generate/index.ts | 6 ++++++ .../oas/generate/methods/do-oas-update.ts | 12 ++++++++++-- .../oas/generate/methods/patch-oas-spec.ts | 17 +++++++++++------ .../generate/methods/update-spec-for-group.ts | 3 +++ .../core/src/implementations/generate-oas.ts | 2 ++ packages/core/src/index.ts | 12 ++++++++++-- 8 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 .changeset/gentle-trees-tan.md diff --git a/.changeset/gentle-trees-tan.md b/.changeset/gentle-trees-tan.md new file mode 100644 index 0000000..f693e38 --- /dev/null +++ b/.changeset/gentle-trees-tan.md @@ -0,0 +1,6 @@ +--- +"@calycode/core": minor +"@calycode/cli": minor +--- + +chore: by default do not generate table schemas into the OAS spec, but allow that via flag diff --git a/packages/cli/src/commands/generate/implementation/oas-spec.ts b/packages/cli/src/commands/generate/implementation/oas-spec.ts index 6ff1d67..1e8f8bc 100644 --- a/packages/cli/src/commands/generate/implementation/oas-spec.ts +++ b/packages/cli/src/commands/generate/implementation/oas-spec.ts @@ -15,6 +15,7 @@ async function updateOasWizard({ isAll = false, printOutput = false, core, + includeTables = false, }: { instance: string; workspace: string; @@ -23,6 +24,7 @@ async function updateOasWizard({ isAll: boolean; printOutput: boolean; core; + includeTables?: boolean; }) { attachCliEventHandlers('generate-oas', core, { instance, @@ -60,7 +62,8 @@ async function updateOasWizard({ workspaceConfig.name, branchConfig.label, groups, - startDir + startDir, + includeTables ); for (const { group, generatedItems } of allGroupResults) { const apiGroupNameNorm = normalizeApiGroupName(group); diff --git a/packages/cli/src/commands/generate/index.ts b/packages/cli/src/commands/generate/index.ts index d1cbb43..4f153c8 100644 --- a/packages/cli/src/commands/generate/index.ts +++ b/packages/cli/src/commands/generate/index.ts @@ -108,6 +108,11 @@ function registerGenerateCommands(program, core) { addApiGroupOptions(specGenCommand); addPrintOutputFlag(specGenCommand); + specGenCommand.option( + '--include-tables', + 'Requests table schema fetching and inclusion into the generate spec. By default tables are not included.' + ); + specGenCommand.action( withErrorHandler(async (opts) => { await updateOasWizard({ @@ -118,6 +123,7 @@ function registerGenerateCommands(program, core) { isAll: opts.all, printOutput: opts.printOutputDir, core: core, + includeTables: opts.includeTables, }); }) ); diff --git a/packages/core/src/features/oas/generate/methods/do-oas-update.ts b/packages/core/src/features/oas/generate/methods/do-oas-update.ts index e8610d6..4aeb38e 100644 --- a/packages/core/src/features/oas/generate/methods/do-oas-update.ts +++ b/packages/core/src/features/oas/generate/methods/do-oas-update.ts @@ -15,15 +15,23 @@ async function doOasUpdate({ inputOas, instanceConfig, workspaceConfig, - storage, // Used for meta lookups, not FS + storage, + includeTables = false, }: { inputOas: any; instanceConfig: any; workspaceConfig: any; storage: any; + includeTables: boolean; }): Promise { // Patch and enrich OAS - const oas = await patchOasSpec({ oas: inputOas, instanceConfig, workspaceConfig, storage }); + const oas = await patchOasSpec({ + oas: inputOas, + instanceConfig, + workspaceConfig, + storage, + includeTables, + }); // Prepare output artifacts (relative paths) const generatedItems: GeneratedItem[] = [ diff --git a/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts b/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts index da75997..c58081b 100644 --- a/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts +++ b/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts @@ -1,15 +1,21 @@ import { cleanupResponseSchemas } from './cleanup-response-schemas'; import { generateTableSchemas } from '..'; -async function patchOasSpec({ oas, instanceConfig, workspaceConfig, storage }) { - +async function patchOasSpec({ + oas, + instanceConfig, + workspaceConfig, + storage, + includeTables = false, +}) { const newOas = { ...oas }; - const tableSchemas = await generateTableSchemas({ instanceConfig, workspaceConfig, storage }); + const tableSchemas = includeTables + ? await generateTableSchemas({ instanceConfig, workspaceConfig, storage }) + : {}; newOas.openapi = '3.1.1'; newOas.components = { - ...(oas.components ?? {}), responses: { @@ -225,7 +231,6 @@ async function patchOasSpec({ oas, instanceConfig, workspaceConfig, storage }) { bearerFormat: 'JWT', }), }, - }; newOas.security = newOas.security || [{ bearerAuth: [] }]; @@ -235,4 +240,4 @@ async function patchOasSpec({ oas, instanceConfig, workspaceConfig, storage }) { return oasWithPatchedResponseSchemas; } -export { patchOasSpec } \ No newline at end of file +export { patchOasSpec }; diff --git a/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts b/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts index 912a5d3..fa54e12 100644 --- a/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts +++ b/packages/core/src/features/oas/generate/methods/update-spec-for-group.ts @@ -12,6 +12,7 @@ async function updateSpecForGroup({ workspaceConfig, storage, core, + includeTables = false, }: { group: any; instanceConfig: any; @@ -19,6 +20,7 @@ async function updateSpecForGroup({ branchConfig: any; storage: any; core: any; + includeTables: boolean; }): Promise<{ oas: string; generatedItems: GeneratedItem[]; @@ -36,6 +38,7 @@ async function updateSpecForGroup({ instanceConfig, workspaceConfig, storage, + includeTables, }); // Optionally emit info for consumer diff --git a/packages/core/src/implementations/generate-oas.ts b/packages/core/src/implementations/generate-oas.ts index 0bcb8fc..61df9ca 100644 --- a/packages/core/src/implementations/generate-oas.ts +++ b/packages/core/src/implementations/generate-oas.ts @@ -10,6 +10,7 @@ async function updateOpenapiSpecImplementation( workspace: string; branch: string; groups: ApiGroup[]; + includeTables?: boolean; }, startDir: string ): Promise<{ group: string; oas: any; generatedItems: { path: string; content: string }[] }[]> { @@ -42,6 +43,7 @@ async function updateOpenapiSpecImplementation( branchConfig, storage, core, + includeTables: options.includeTables ?? false, }); return { group: grp.name, oas, generatedItems }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e038fa9..607c70f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -146,7 +146,8 @@ export class Caly extends TypedEmitter { workspace: string, branch: string, groups: any, - startDir: string + startDir: string, + includeTables?: boolean ): Promise<{ group: string; oas: any; generatedItems: { path: string; content: string }[] }[]> { return updateOpenapiSpecImplementation( this.storage, @@ -156,6 +157,7 @@ export class Caly extends TypedEmitter { workspace, branch, groups, + includeTables, }, startDir ); @@ -386,7 +388,12 @@ export class Caly extends TypedEmitter { * }); * ``` */ - async doOasUpdate({ inputOas, instanceConfig, workspaceConfig }): Promise<{ + async doOasUpdate({ + inputOas, + instanceConfig, + workspaceConfig, + includeTables = false, + }): Promise<{ oas: any; generatedItems: { path: string; @@ -398,6 +405,7 @@ export class Caly extends TypedEmitter { instanceConfig, workspaceConfig, storage: this.storage, + includeTables, }); } From d9da43b1288c476899cd8833166b09eae34f6e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Mon, 1 Dec 2025 21:44:30 +0100 Subject: [PATCH 3/3] chore: oas spec fixes, to satisfy spectral rules (without modifying the actual oas state from Xano) --- .changeset/tangy-windows-taste.md | 8 ++++ .../methods/extract-tags-to-global-level.ts | 29 ++++++++++++++ .../oas/generate/methods/patch-oas-spec.ts | 39 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 .changeset/tangy-windows-taste.md create mode 100644 packages/core/src/features/oas/generate/methods/extract-tags-to-global-level.ts diff --git a/.changeset/tangy-windows-taste.md b/.changeset/tangy-windows-taste.md new file mode 100644 index 0000000..3ae76e0 --- /dev/null +++ b/.changeset/tangy-windows-taste.md @@ -0,0 +1,8 @@ +--- +'@calycode/core': patch +'@calycode/cli': patch +--- + +chore: extract the tags from the paths to the global to comply better with OAS +chore: set maxLength to strings on the error codes (owasp:api4:2019-string-limit) +chore: set type to strings (owasp:api4:2019-string-restricted) diff --git a/packages/core/src/features/oas/generate/methods/extract-tags-to-global-level.ts b/packages/core/src/features/oas/generate/methods/extract-tags-to-global-level.ts new file mode 100644 index 0000000..15f8435 --- /dev/null +++ b/packages/core/src/features/oas/generate/methods/extract-tags-to-global-level.ts @@ -0,0 +1,29 @@ +function extractTagsToGlobal(paths) { + // 1. Collect all tags used in operations + const tagSet = new Set(); + for (const [path, methods] of Object.entries(paths || {})) { + for (const [method, operation] of Object.entries(methods)) { + if (operation.tags && Array.isArray(operation.tags)) { + operation.tags.forEach((tag) => tagSet.add(tag)); + } + } + } + + // 2. Build the global tags array if not present + let tags = Array.from(tagSet).map((tag) => ({ + name: tag, + description: `Auto-generated tag for ${tag}`, + })); + + // (Optional) If you want to preserve existing tags and only add missing ones: + const existingTags = (tags || []).map((t) => t.name); + const allTags = Array.from(new Set([...existingTags, ...tagSet])); + tags = allTags.map((tag) => ({ + name: tag, + description: `Auto-generated tag for ${tag}`, + })); + + return tags; +} + +export { extractTagsToGlobal }; diff --git a/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts b/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts index c58081b..3c0b377 100644 --- a/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts +++ b/packages/core/src/features/oas/generate/methods/patch-oas-spec.ts @@ -1,4 +1,5 @@ import { cleanupResponseSchemas } from './cleanup-response-schemas'; +import { extractTagsToGlobal } from './extract-tags-to-global-level'; import { generateTableSchemas } from '..'; async function patchOasSpec({ @@ -15,6 +16,8 @@ async function patchOasSpec({ newOas.openapi = '3.1.1'; + newOas.tags = extractTagsToGlobal(newOas.paths); + newOas.components = { ...(oas.components ?? {}), @@ -89,16 +92,22 @@ async function patchOasSpec({ properties: { code: { type: 'string', + format: 'const', + maxLength: 64, example: 'ERROR_CODE_ACCESS_DENIED', }, message: { type: 'string', + format: 'const', + maxLength: 256, example: 'Forbidden access.', }, payload: { anyOf: [ { type: 'string', + format: 'const', + maxLength: 1024, }, { type: 'null' }, { type: 'object', properties: {}, additionalProperties: true }, @@ -112,16 +121,22 @@ async function patchOasSpec({ properties: { code: { type: 'string', + format: 'const', + maxLength: 64, example: 'ERROR_CODE_UNAUTHORIZED', }, message: { type: 'string', + format: 'const', + maxLength: 256, example: 'Authentication required.', }, payload: { anyOf: [ { type: 'string', + format: 'const', + maxLength: 1024, }, { type: 'null' }, { type: 'object', properties: {}, additionalProperties: true }, @@ -135,16 +150,22 @@ async function patchOasSpec({ properties: { code: { type: 'string', + format: 'const', + maxLength: 64, example: 'ERROR_FATAL', }, message: { type: 'string', + format: 'const', + maxLength: 256, example: 'Something went wrong.', }, payload: { anyOf: [ { type: 'string', + format: 'const', + maxLength: 1024, }, { type: 'null' }, { type: 'object', properties: {}, additionalProperties: true }, @@ -158,16 +179,22 @@ async function patchOasSpec({ properties: { code: { type: 'string', + format: 'const', + maxLength: 64, example: 'ERROR_CODE_TOO_MANY_REQUESTS', }, message: { type: 'string', + format: 'const', + maxLength: 256, example: 'Hit quota limits.', }, payload: { anyOf: [ { type: 'string', + format: 'const', + maxLength: 1024, }, { type: 'null' }, { type: 'object', properties: {}, additionalProperties: true }, @@ -181,16 +208,22 @@ async function patchOasSpec({ properties: { code: { type: 'string', + format: 'const', + maxLength: 64, example: 'ERROR_CODE_NOT_FOUND', }, message: { type: 'string', + format: 'const', + maxLength: 256, example: 'The requested resource cannot be found.', }, payload: { anyOf: [ { type: 'string', + format: 'const', + maxLength: 1024, }, { type: 'null' }, { type: 'object', properties: {}, additionalProperties: true }, @@ -204,16 +237,22 @@ async function patchOasSpec({ properties: { code: { type: 'string', + format: 'const', + maxLength: 64, example: 'ERROR_CODE_BAD_REQUEST', }, message: { type: 'string', + format: 'const', + maxLength: 256, example: 'The provided inputs are not correct.', }, payload: { anyOf: [ { type: 'string', + format: 'const', + maxLength: 1024, }, { type: 'null' }, { type: 'object', properties: {}, additionalProperties: true },