From 2078040ab49df3b940d3139888a740ba2f8ebb62 Mon Sep 17 00:00:00 2001 From: Roberto Bianchi Date: Fri, 6 Nov 2026 14:38:35 +0100 Subject: [PATCH] perf(openapi): use set for operation ids Signed-off-by: Roberto Bianchi --- .../massimo-cli/lib/frontend-openapi-generator.js | 2 +- packages/massimo-cli/lib/openapi-generator.js | 2 +- packages/massimo/index.d.ts | 2 +- packages/massimo/index.js | 11 ++++++++--- packages/massimo/test/generateOperationId.test.js | 14 ++++++++++++++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/massimo-cli/lib/frontend-openapi-generator.js b/packages/massimo-cli/lib/frontend-openapi-generator.js index 653a3c74..d8bd0c32 100644 --- a/packages/massimo-cli/lib/frontend-openapi-generator.js +++ b/packages/massimo-cli/lib/frontend-openapi-generator.js @@ -398,7 +398,7 @@ function generateFrontendImplementationFromOpenAPI ({ function generateTypesFromOpenAPI ({ schema, name, fullRequest, fullResponse, propsOptional, namedSchemas }) { const camelCaseName = capitalize(camelcase(name)) const { paths } = schema - const generatedOperationIds = [] + const generatedOperationIds = new Set() const operations = Object.entries(paths).flatMap(([path, methods]) => { return Object.entries(methods).map(([method, operation]) => { const opId = generateOperationId(path, method, operation, generatedOperationIds) diff --git a/packages/massimo-cli/lib/openapi-generator.js b/packages/massimo-cli/lib/openapi-generator.js index 7810e55f..ad283962 100644 --- a/packages/massimo-cli/lib/openapi-generator.js +++ b/packages/massimo-cli/lib/openapi-generator.js @@ -88,7 +88,7 @@ function generateTypesFromOpenAPI ({ const camelcasedName = toJavaScriptName(name) const capitalizedName = capitalize(camelcasedName) const { paths } = schema - const generatedOperationIds = [] + const generatedOperationIds = new Set() const operations = Object.entries(paths).flatMap(([path, methods]) => { let commonParameters = [] diff --git a/packages/massimo/index.d.ts b/packages/massimo/index.d.ts index 7076b446..e3bb951b 100644 --- a/packages/massimo/index.d.ts +++ b/packages/massimo/index.d.ts @@ -75,7 +75,7 @@ type OpenTelemetry = { setErrorInSpanClient?: (span: any, err: unknown) => void } -export function generateOperationId(path: string, method: string, methodMeta: MethodMetaInterface, all: string[]): string +export function generateOperationId(path: string, method: string, methodMeta: MethodMetaInterface, all: string[] | Set): string export function buildOpenAPIClient(options: BuildOpenAPIClientOptions, openTelemetry?: OpenTelemetry): Promise export function buildGraphQLClient(options: BuildGraphQLClientOptions, openTelemetry?: OpenTelemetry, logger?: AbstractLogger): Promise export function hasDuplicatedParameters(methodMeta: MethodMetaInterface): boolean diff --git a/packages/massimo/index.js b/packages/massimo/index.js index 8492f32f..e0de9e3e 100644 --- a/packages/massimo/index.js +++ b/packages/massimo/index.js @@ -358,6 +358,7 @@ function wrapGraphQLClient (url, openTelemetry, logger) { } export function generateOperationId (path, method, methodMeta, all) { + const hasOperationId = candidate => all instanceof Set ? all.has(candidate) : all.includes(candidate) let operationId = null // use methodMeta.operationId only if it's present AND it is a valid string that can be // concatenated without converting it @@ -385,7 +386,7 @@ export function generateOperationId (path, method, methodMeta, all) { } else { let count = 0 let candidate = operationId - while (all.includes(candidate)) { + while (hasOperationId(candidate)) { if (count === 0) { // first try with method name candidate = `${method}${capitalize(operationId)}` @@ -397,7 +398,11 @@ export function generateOperationId (path, method, methodMeta, all) { operationId = candidate } operationId = camelCase(operationId) - all.push(operationId) + if (all instanceof Set) { + all.add(operationId) + } else { + all.push(operationId) + } return operationId } @@ -441,7 +446,7 @@ export async function buildOpenAPIClient (options, openTelemetry) { dispatcher = dispatcher.compose(interceptors.responseError()) } - const generatedOperationIds = [] + const generatedOperationIds = new Set() for (const path of Object.keys(spec.paths)) { const pathMeta = spec.paths[path] let commonParameters = [] diff --git a/packages/massimo/test/generateOperationId.test.js b/packages/massimo/test/generateOperationId.test.js index 1ffbea0e..b4f8b68c 100644 --- a/packages/massimo/test/generateOperationId.test.js +++ b/packages/massimo/test/generateOperationId.test.js @@ -45,6 +45,20 @@ test('generates name from different path with same method and opeartionId', asyn equal(fourth, 'getSampleOperationId2') }) +test('supports a Set for efficient operation ID tracking', () => { + const ids = new Set() + const first = generateOperationId('sample-path', 'get', { operationId: 'sampleOperationId' }, ids) + const second = generateOperationId('sample-path2', 'get', { operationId: 'sampleOperationId' }, ids) + const third = generateOperationId('sample-path3', 'get', { operationId: 'sampleOperationId' }, ids) + + equal(first, 'sampleOperationId') + equal(second, 'getSampleOperationId') + equal(third, 'getSampleOperationId1') + equal(ids.has(first), true) + equal(ids.has(second), true) + equal(ids.has(third), true) +}) + test('generates name with path parameter', async t => { const opId = generateOperationId('/v3/accounts/{id}', 'get', {}, []) equal(opId, 'getV3AccountsId')