diff --git a/src/services/publish-service.ts b/src/services/publish-service.ts index 0184ec67..8aa07470 100644 --- a/src/services/publish-service.ts +++ b/src/services/publish-service.ts @@ -344,19 +344,23 @@ async function executePuts( } } else if (tier === 2) { const tier2Descriptors = filterApiRevisionsHandledByRootApis(descriptors); + const apiDescriptors = tier2Descriptors.filter((d) => d.type === ResourceType.Api); + const nonApiDescriptors = tier2Descriptors.filter((d) => d.type !== ResourceType.Api); - const { mcpApis, regularTier2 } = await splitMcpApis( + const { mcpApis, regularTier2: regularApis } = await splitMcpApis( store, config.sourceDir, - tier2Descriptors + apiDescriptors ); - await publishAndOutput(client, store, context, config, regularTier2, results); + await publishAndOutput(client, store, context, config, regularApis, results); if (mcpApis.length > 0) { - logger.debug(`Publishing ${mcpApis.length} MCP API resource(s) after regular tier 2 resources`); + logger.debug(`Publishing ${mcpApis.length} MCP API resource(s) after regular APIs`); await publishAndOutput(client, store, context, config, mcpApis, results); } + + await publishAndOutput(client, store, context, config, nonApiDescriptors, results); } else { // For tiers 3/4, exclude child resources whose parent is being published // in tier 2 (publishApi/publishProduct handle their children internally). diff --git a/tests/unit/services/publish-service.test.ts b/tests/unit/services/publish-service.test.ts index 013322f8..930fc22b 100644 --- a/tests/unit/services/publish-service.test.ts +++ b/tests/unit/services/publish-service.test.ts @@ -248,6 +248,48 @@ describe('publish-service', () => { expect(apiCallOrder).toEqual(['src-rest-openapi', 'src-mcp-from-api']); }); + it('should wait for APIs to finish publishing before publishing Products in tier 2', async () => { + const resources: ResourceDescriptor[] = [ + { type: ResourceType.Product, nameParts: ['petstore-product'] }, + { type: ResourceType.Api, nameParts: ['swagger-petstore'] }, + ]; + const client = createMockClient(); + const store = createMockStore(resources); + let finishApi!: () => void; + const apiFinished = new Promise((resolve) => { + finishApi = resolve; + }); + + vi.mocked(publishApi).mockImplementation(async (_client, _store, _context, descriptor) => { + await apiFinished; + return { + descriptor, + status: 'success', + action: 'put', + }; + }); + + const config: PublishConfig = { + service: testContext, + sourceDir: '/source', + dryRun: false, + deleteUnmatched: false, + logLevel: LogLevel.INFO, + }; + + const publishPromise = runPublish(client, store, config); + await vi.waitFor(() => expect(publishApi).toHaveBeenCalledOnce()); + + try { + expect(publishProduct).not.toHaveBeenCalled(); + } finally { + finishApi(); + await publishPromise; + } + + expect(publishProduct).toHaveBeenCalledOnce(); + }); + it('should not publish revision APIs as standalone resources when root API is in the same batch', async () => { const resources: ResourceDescriptor[] = [ { type: ResourceType.Api, nameParts: ['orders-api;rev=2'] },