@peterhauge/apiops-cli 0.1.3-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +135 -0
- package/dist/cli/extract-command.d.ts +12 -0
- package/dist/cli/extract-command.d.ts.map +1 -0
- package/dist/cli/extract-command.js +157 -0
- package/dist/cli/extract-command.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +74 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-command.d.ts +11 -0
- package/dist/cli/init-command.d.ts.map +1 -0
- package/dist/cli/init-command.js +87 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli/publish-command.d.ts +12 -0
- package/dist/cli/publish-command.d.ts.map +1 -0
- package/dist/cli/publish-command.js +159 -0
- package/dist/cli/publish-command.js.map +1 -0
- package/dist/clients/apim-client.d.ts +110 -0
- package/dist/clients/apim-client.d.ts.map +1 -0
- package/dist/clients/apim-client.js +586 -0
- package/dist/clients/apim-client.js.map +1 -0
- package/dist/clients/artifact-store.d.ts +23 -0
- package/dist/clients/artifact-store.d.ts.map +1 -0
- package/dist/clients/artifact-store.js +188 -0
- package/dist/clients/artifact-store.js.map +1 -0
- package/dist/clients/iapim-client.d.ts +52 -0
- package/dist/clients/iapim-client.d.ts.map +1 -0
- package/dist/clients/iapim-client.js +6 -0
- package/dist/clients/iapim-client.js.map +1 -0
- package/dist/clients/iartifact-store.d.ts +50 -0
- package/dist/clients/iartifact-store.d.ts.map +1 -0
- package/dist/clients/iartifact-store.js +6 -0
- package/dist/clients/iartifact-store.js.map +1 -0
- package/dist/lib/auto-generated.d.ts +27 -0
- package/dist/lib/auto-generated.d.ts.map +1 -0
- package/dist/lib/auto-generated.js +34 -0
- package/dist/lib/auto-generated.js.map +1 -0
- package/dist/lib/cloud-config.d.ts +29 -0
- package/dist/lib/cloud-config.d.ts.map +1 -0
- package/dist/lib/cloud-config.js +60 -0
- package/dist/lib/cloud-config.js.map +1 -0
- package/dist/lib/config-loader.d.ts +21 -0
- package/dist/lib/config-loader.d.ts.map +1 -0
- package/dist/lib/config-loader.js +131 -0
- package/dist/lib/config-loader.js.map +1 -0
- package/dist/lib/dependency-graph.d.ts +43 -0
- package/dist/lib/dependency-graph.d.ts.map +1 -0
- package/dist/lib/dependency-graph.js +163 -0
- package/dist/lib/dependency-graph.js.map +1 -0
- package/dist/lib/exit-codes.d.ts +27 -0
- package/dist/lib/exit-codes.d.ts.map +1 -0
- package/dist/lib/exit-codes.js +33 -0
- package/dist/lib/exit-codes.js.map +1 -0
- package/dist/lib/logger.d.ts +39 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +128 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/parallel-runner.d.ts +38 -0
- package/dist/lib/parallel-runner.d.ts.map +1 -0
- package/dist/lib/parallel-runner.js +70 -0
- package/dist/lib/parallel-runner.js.map +1 -0
- package/dist/lib/resource-path.d.ts +205 -0
- package/dist/lib/resource-path.d.ts.map +1 -0
- package/dist/lib/resource-path.js +401 -0
- package/dist/lib/resource-path.js.map +1 -0
- package/dist/lib/resource-uri.d.ts +40 -0
- package/dist/lib/resource-uri.d.ts.map +1 -0
- package/dist/lib/resource-uri.js +86 -0
- package/dist/lib/resource-uri.js.map +1 -0
- package/dist/lib/user-agent.d.ts +2 -0
- package/dist/lib/user-agent.d.ts.map +1 -0
- package/dist/lib/user-agent.js +5 -0
- package/dist/lib/user-agent.js.map +1 -0
- package/dist/models/config.d.ts +83 -0
- package/dist/models/config.d.ts.map +1 -0
- package/dist/models/config.js +6 -0
- package/dist/models/config.js.map +1 -0
- package/dist/models/resource-types.d.ts +66 -0
- package/dist/models/resource-types.d.ts.map +1 -0
- package/dist/models/resource-types.js +243 -0
- package/dist/models/resource-types.js.map +1 -0
- package/dist/models/types.d.ts +47 -0
- package/dist/models/types.d.ts.map +1 -0
- package/dist/models/types.js +6 -0
- package/dist/models/types.js.map +1 -0
- package/dist/services/api-extractor.d.ts +36 -0
- package/dist/services/api-extractor.d.ts.map +1 -0
- package/dist/services/api-extractor.js +319 -0
- package/dist/services/api-extractor.js.map +1 -0
- package/dist/services/api-publisher.d.ts +18 -0
- package/dist/services/api-publisher.d.ts.map +1 -0
- package/dist/services/api-publisher.js +290 -0
- package/dist/services/api-publisher.js.map +1 -0
- package/dist/services/delete-unmatched-service.d.ts +17 -0
- package/dist/services/delete-unmatched-service.d.ts.map +1 -0
- package/dist/services/delete-unmatched-service.js +143 -0
- package/dist/services/delete-unmatched-service.js.map +1 -0
- package/dist/services/dry-run-reporter.d.ts +30 -0
- package/dist/services/dry-run-reporter.d.ts.map +1 -0
- package/dist/services/dry-run-reporter.js +111 -0
- package/dist/services/dry-run-reporter.js.map +1 -0
- package/dist/services/extract-service.d.ts +47 -0
- package/dist/services/extract-service.d.ts.map +1 -0
- package/dist/services/extract-service.js +374 -0
- package/dist/services/extract-service.js.map +1 -0
- package/dist/services/filter-service.d.ts +29 -0
- package/dist/services/filter-service.d.ts.map +1 -0
- package/dist/services/filter-service.js +143 -0
- package/dist/services/filter-service.js.map +1 -0
- package/dist/services/git-diff-service.d.ts +23 -0
- package/dist/services/git-diff-service.d.ts.map +1 -0
- package/dist/services/git-diff-service.js +135 -0
- package/dist/services/git-diff-service.js.map +1 -0
- package/dist/services/identity-guide-service.d.ts +11 -0
- package/dist/services/identity-guide-service.d.ts.map +1 -0
- package/dist/services/identity-guide-service.js +227 -0
- package/dist/services/identity-guide-service.js.map +1 -0
- package/dist/services/init-service.d.ts +16 -0
- package/dist/services/init-service.d.ts.map +1 -0
- package/dist/services/init-service.js +304 -0
- package/dist/services/init-service.js.map +1 -0
- package/dist/services/keyvault-checker.d.ts +58 -0
- package/dist/services/keyvault-checker.d.ts.map +1 -0
- package/dist/services/keyvault-checker.js +390 -0
- package/dist/services/keyvault-checker.js.map +1 -0
- package/dist/services/override-merger.d.ts +20 -0
- package/dist/services/override-merger.d.ts.map +1 -0
- package/dist/services/override-merger.js +102 -0
- package/dist/services/override-merger.js.map +1 -0
- package/dist/services/product-extractor.d.ts +26 -0
- package/dist/services/product-extractor.d.ts.map +1 -0
- package/dist/services/product-extractor.js +141 -0
- package/dist/services/product-extractor.js.map +1 -0
- package/dist/services/product-publisher.d.ts +15 -0
- package/dist/services/product-publisher.d.ts.map +1 -0
- package/dist/services/product-publisher.js +113 -0
- package/dist/services/product-publisher.js.map +1 -0
- package/dist/services/prompt-service.d.ts +13 -0
- package/dist/services/prompt-service.d.ts.map +1 -0
- package/dist/services/prompt-service.js +69 -0
- package/dist/services/prompt-service.js.map +1 -0
- package/dist/services/publish-service.d.ts +31 -0
- package/dist/services/publish-service.d.ts.map +1 -0
- package/dist/services/publish-service.js +445 -0
- package/dist/services/publish-service.js.map +1 -0
- package/dist/services/resource-extractor.d.ts +52 -0
- package/dist/services/resource-extractor.d.ts.map +1 -0
- package/dist/services/resource-extractor.js +168 -0
- package/dist/services/resource-extractor.js.map +1 -0
- package/dist/services/resource-publisher.d.ts +23 -0
- package/dist/services/resource-publisher.d.ts.map +1 -0
- package/dist/services/resource-publisher.js +349 -0
- package/dist/services/resource-publisher.js.map +1 -0
- package/dist/services/secret-redactor.d.ts +20 -0
- package/dist/services/secret-redactor.d.ts.map +1 -0
- package/dist/services/secret-redactor.js +45 -0
- package/dist/services/secret-redactor.js.map +1 -0
- package/dist/services/transitive-resolver.d.ts +45 -0
- package/dist/services/transitive-resolver.d.ts.map +1 -0
- package/dist/services/transitive-resolver.js +177 -0
- package/dist/services/transitive-resolver.js.map +1 -0
- package/dist/services/workspace-extractor.d.ts +34 -0
- package/dist/services/workspace-extractor.d.ts.map +1 -0
- package/dist/services/workspace-extractor.js +120 -0
- package/dist/services/workspace-extractor.js.map +1 -0
- package/dist/templates/azure-devops/extract-pipeline.d.ts +9 -0
- package/dist/templates/azure-devops/extract-pipeline.d.ts.map +1 -0
- package/dist/templates/azure-devops/extract-pipeline.js +95 -0
- package/dist/templates/azure-devops/extract-pipeline.js.map +1 -0
- package/dist/templates/azure-devops/publish-pipeline.d.ts +10 -0
- package/dist/templates/azure-devops/publish-pipeline.d.ts.map +1 -0
- package/dist/templates/azure-devops/publish-pipeline.js +100 -0
- package/dist/templates/azure-devops/publish-pipeline.js.map +1 -0
- package/dist/templates/configs/filter-config.d.ts +6 -0
- package/dist/templates/configs/filter-config.d.ts.map +1 -0
- package/dist/templates/configs/filter-config.js +51 -0
- package/dist/templates/configs/filter-config.js.map +1 -0
- package/dist/templates/configs/override-config.d.ts +6 -0
- package/dist/templates/configs/override-config.d.ts.map +1 -0
- package/dist/templates/configs/override-config.js +45 -0
- package/dist/templates/configs/override-config.js.map +1 -0
- package/dist/templates/configs/package-json.d.ts +10 -0
- package/dist/templates/configs/package-json.d.ts.map +1 -0
- package/dist/templates/configs/package-json.js +19 -0
- package/dist/templates/configs/package-json.js.map +1 -0
- package/dist/templates/copilot/identity-setup-prompt.d.ts +13 -0
- package/dist/templates/copilot/identity-setup-prompt.d.ts.map +1 -0
- package/dist/templates/copilot/identity-setup-prompt.js +279 -0
- package/dist/templates/copilot/identity-setup-prompt.js.map +1 -0
- package/dist/templates/github-actions/extract-workflow.d.ts +9 -0
- package/dist/templates/github-actions/extract-workflow.d.ts.map +1 -0
- package/dist/templates/github-actions/extract-workflow.js +126 -0
- package/dist/templates/github-actions/extract-workflow.js.map +1 -0
- package/dist/templates/github-actions/publish-workflow.d.ts +10 -0
- package/dist/templates/github-actions/publish-workflow.d.ts.map +1 -0
- package/dist/templates/github-actions/publish-workflow.js +105 -0
- package/dist/templates/github-actions/publish-workflow.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T022: API-specific extraction logic
|
|
3
|
+
* API revisions, API specifications, operations & operation policies,
|
|
4
|
+
* GraphQL resolvers & resolver policies, API tags, diagnostics, schemas,
|
|
5
|
+
* releases, tag descriptions, wikis.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceType } from '../models/resource-types.js';
|
|
8
|
+
import { shouldIncludeResource } from './filter-service.js';
|
|
9
|
+
import { extractResourceType } from './resource-extractor.js';
|
|
10
|
+
import { logger } from '../lib/logger.js';
|
|
11
|
+
import { buildResourceLabel } from '../lib/resource-uri.js';
|
|
12
|
+
import { getNamePart } from '../lib/resource-path.js';
|
|
13
|
+
/**
|
|
14
|
+
* Extract all API-specific resources for a single API.
|
|
15
|
+
* This includes revisions, specifications, operations, policies, etc.
|
|
16
|
+
*/
|
|
17
|
+
export async function extractApiResources(client, store, context, apiDescriptor, apiJson, outputDir, filter, workspace) {
|
|
18
|
+
const apiName = getNamePart(apiDescriptor.nameParts, 0);
|
|
19
|
+
const result = {
|
|
20
|
+
apiName,
|
|
21
|
+
revisions: [],
|
|
22
|
+
specification: false,
|
|
23
|
+
operations: [],
|
|
24
|
+
operationPolicies: [],
|
|
25
|
+
tags: [],
|
|
26
|
+
diagnostics: [],
|
|
27
|
+
schemas: [],
|
|
28
|
+
releases: [],
|
|
29
|
+
tagDescriptions: [],
|
|
30
|
+
wiki: false,
|
|
31
|
+
resolvers: [],
|
|
32
|
+
resolverPolicies: [],
|
|
33
|
+
policies: [],
|
|
34
|
+
};
|
|
35
|
+
// Extract API revisions
|
|
36
|
+
result.revisions = await extractApiRevisions(client, store, context, apiName, outputDir, filter, workspace);
|
|
37
|
+
// Extract API schemas FIRST. For synthetic GraphQL APIs the SDL lives in an
|
|
38
|
+
// ApiSchema resource; by extracting schemas first we can detect that case
|
|
39
|
+
// from the results and skip the (failing) spec export — avoiding a redundant
|
|
40
|
+
// `list schemas` probe per GraphQL-typed API at scale.
|
|
41
|
+
const schemaResult = await extractResourceType(client, store, context, ResourceType.ApiSchema, outputDir, filter, apiDescriptor, workspace);
|
|
42
|
+
result.schemas = schemaResult.extracted;
|
|
43
|
+
// Extract API specification (uses already-extracted schemas to detect
|
|
44
|
+
// synthetic GraphQL without a second list call).
|
|
45
|
+
result.specification = await extractApiSpecification(client, store, context, apiDescriptor, apiJson, outputDir, result.schemas);
|
|
46
|
+
// Extract API policy
|
|
47
|
+
const policyContent = await extractApiPolicy(client, store, context, apiDescriptor, outputDir);
|
|
48
|
+
if (policyContent) {
|
|
49
|
+
result.policies.push(policyContent);
|
|
50
|
+
}
|
|
51
|
+
// Extract operations and their policies
|
|
52
|
+
const opsResult = await extractApiOperations(client, store, context, apiDescriptor, outputDir, filter, workspace);
|
|
53
|
+
result.operations = opsResult.operations;
|
|
54
|
+
result.operationPolicies = opsResult.operationPolicies;
|
|
55
|
+
result.policies.push(...opsResult.policies);
|
|
56
|
+
// Extract API tags
|
|
57
|
+
const tagsResult = await extractResourceType(client, store, context, ResourceType.ApiTag, outputDir, filter, apiDescriptor, workspace);
|
|
58
|
+
result.tags = tagsResult.extracted;
|
|
59
|
+
// Extract API diagnostics
|
|
60
|
+
const diagResult = await extractResourceType(client, store, context, ResourceType.ApiDiagnostic, outputDir, filter, apiDescriptor, workspace);
|
|
61
|
+
result.diagnostics = diagResult.extracted;
|
|
62
|
+
// Extract API releases
|
|
63
|
+
const releaseResult = await extractResourceType(client, store, context, ResourceType.ApiRelease, outputDir, filter, apiDescriptor, workspace);
|
|
64
|
+
result.releases = releaseResult.extracted;
|
|
65
|
+
// Extract API tag descriptions
|
|
66
|
+
const tagDescResult = await extractResourceType(client, store, context, ResourceType.ApiTagDescription, outputDir, filter, apiDescriptor, workspace);
|
|
67
|
+
result.tagDescriptions = tagDescResult.extracted;
|
|
68
|
+
// Extract API wiki
|
|
69
|
+
result.wiki = await extractApiWiki(client, store, context, apiDescriptor, outputDir);
|
|
70
|
+
// Extract GraphQL resolvers and their policies
|
|
71
|
+
const resolverResult = await extractGraphQLResolvers(client, store, context, apiDescriptor, apiJson, outputDir, filter, workspace);
|
|
72
|
+
result.resolvers = resolverResult.resolvers;
|
|
73
|
+
result.resolverPolicies = resolverResult.resolverPolicies;
|
|
74
|
+
result.policies.push(...resolverResult.policies);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Extract API revisions.
|
|
79
|
+
* Lists revisions and extracts each as a sub-folder with ;rev=N naming.
|
|
80
|
+
*/
|
|
81
|
+
async function extractApiRevisions(client, store, context, apiName, outputDir, filter, workspace) {
|
|
82
|
+
const results = [];
|
|
83
|
+
try {
|
|
84
|
+
const revisions = client.listApiRevisions(context, apiName);
|
|
85
|
+
for await (const revision of revisions) {
|
|
86
|
+
try {
|
|
87
|
+
const revNumber = (revision.apiRevision ?? revision.revisionNumber);
|
|
88
|
+
if (!revNumber || revNumber === '1') {
|
|
89
|
+
// Skip revision 1 — it's the main API
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const revName = `${apiName};rev=${revNumber}`;
|
|
93
|
+
const descriptor = {
|
|
94
|
+
type: ResourceType.Api,
|
|
95
|
+
nameParts: [revName],
|
|
96
|
+
workspace,
|
|
97
|
+
};
|
|
98
|
+
// Check filter — use root API name for matching
|
|
99
|
+
if (!shouldIncludeResource(descriptor, filter)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// Get full revision resource
|
|
103
|
+
const revJson = await client.getResource(context, descriptor);
|
|
104
|
+
if (revJson) {
|
|
105
|
+
await store.writeResource(outputDir, descriptor, revJson);
|
|
106
|
+
results.push({ descriptor, json: revJson, status: 'success' });
|
|
107
|
+
logger.info(`Extracted revision ${buildResourceLabel(descriptor)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
112
|
+
logger.warn(`Failed to extract revision: ${errorMessage}`);
|
|
113
|
+
results.push({
|
|
114
|
+
descriptor: { type: ResourceType.Api, nameParts: [`${apiName};rev=?`] },
|
|
115
|
+
json: {},
|
|
116
|
+
status: 'error',
|
|
117
|
+
error: errorMessage,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
logger.warn(`Failed to list revisions for API "${apiName}": ${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Extract API specification (OpenAPI/GraphQL/WSDL/WADL).
|
|
129
|
+
* WebSocket APIs do not have an OpenAPI specification — skip with a debug log.
|
|
130
|
+
* Synthetic GraphQL APIs (schema stored as an ApiSchema, no external SDL blob)
|
|
131
|
+
* are detected by inspecting already-extracted schemas and skipped here —
|
|
132
|
+
* their schema is captured by the ApiSchema extraction step. Pass-through
|
|
133
|
+
* GraphQL APIs (linked to an external GraphQL server) export their SDL via
|
|
134
|
+
* the graphql-link format.
|
|
135
|
+
*/
|
|
136
|
+
async function extractApiSpecification(client, store, context, apiDescriptor, apiJson, outputDir, extractedSchemas) {
|
|
137
|
+
const properties = apiJson.properties;
|
|
138
|
+
const apiType = properties?.type;
|
|
139
|
+
if (apiType?.toLowerCase() === 'websocket') {
|
|
140
|
+
logger.debug(`OpenAPI does not apply to WebSocket APIs`);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (apiType?.toLowerCase() === 'graphql' && hasGraphQLSchema(extractedSchemas)) {
|
|
144
|
+
logger.debug(`Skipping spec export for synthetic GraphQL API "${getNamePart(apiDescriptor.nameParts, 0)}" — schema is captured via ApiSchema`);
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const spec = await client.getApiSpecification(context, getNamePart(apiDescriptor.nameParts, 0), apiType);
|
|
149
|
+
if (!spec) {
|
|
150
|
+
logger.debug(`No specification found for API "${getNamePart(apiDescriptor.nameParts, 0)}"`);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
await store.writeContent(outputDir, apiDescriptor, spec.content, 'specification', spec.format);
|
|
154
|
+
logger.info(`Extracted specification ${buildResourceLabel(apiDescriptor)} (${spec.format})`);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
159
|
+
logger.warn(`Failed to extract specification ${buildResourceLabel(apiDescriptor)}: ${errorMessage}`);
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns true if any already-extracted ApiSchema resource has a contentType
|
|
165
|
+
* indicating a GraphQL schema. Used to distinguish synthetic GraphQL APIs
|
|
166
|
+
* (schema stored in APIM) from pass-through GraphQL APIs (schema fetched from
|
|
167
|
+
* backend). Inspects schemas that were extracted prior to spec export, so no
|
|
168
|
+
* extra list call is required.
|
|
169
|
+
*/
|
|
170
|
+
function hasGraphQLSchema(schemas) {
|
|
171
|
+
for (const schema of schemas) {
|
|
172
|
+
const props = schema.json.properties;
|
|
173
|
+
const contentType = props?.contentType?.toLowerCase() ?? '';
|
|
174
|
+
if (contentType.includes('graphql')) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Extract API-level policy.
|
|
182
|
+
*/
|
|
183
|
+
async function extractApiPolicy(client, store, context, apiDescriptor, outputDir) {
|
|
184
|
+
const policyDescriptor = {
|
|
185
|
+
type: ResourceType.ApiPolicy,
|
|
186
|
+
nameParts: [...apiDescriptor.nameParts],
|
|
187
|
+
workspace: apiDescriptor.workspace,
|
|
188
|
+
};
|
|
189
|
+
const policyJson = await client.getResource(context, policyDescriptor);
|
|
190
|
+
if (!policyJson) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
const properties = policyJson.properties;
|
|
194
|
+
const policyContent = properties?.value;
|
|
195
|
+
if (policyContent) {
|
|
196
|
+
await store.writeContent(outputDir, policyDescriptor, policyContent, 'policy');
|
|
197
|
+
logger.debug(`Extracted ${buildResourceLabel(policyDescriptor)}`);
|
|
198
|
+
return policyContent;
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Extract API operations and their policies.
|
|
204
|
+
*/
|
|
205
|
+
async function extractApiOperations(client, store, context, apiDescriptor, outputDir, filter, workspace) {
|
|
206
|
+
const operations = [];
|
|
207
|
+
const operationPolicies = [];
|
|
208
|
+
const policies = [];
|
|
209
|
+
// Extract operations
|
|
210
|
+
const opsResult = await extractResourceType(client, store, context, ResourceType.ApiOperation, outputDir, filter, apiDescriptor, workspace);
|
|
211
|
+
operations.push(...opsResult.extracted);
|
|
212
|
+
// Extract operation policies for each operation
|
|
213
|
+
for (const op of opsResult.extracted) {
|
|
214
|
+
if (op.status !== 'success')
|
|
215
|
+
continue;
|
|
216
|
+
const opPolicyDescriptor = {
|
|
217
|
+
type: ResourceType.ApiOperationPolicy,
|
|
218
|
+
nameParts: [...op.descriptor.nameParts],
|
|
219
|
+
workspace,
|
|
220
|
+
};
|
|
221
|
+
const policyJson = await client.getResource(context, opPolicyDescriptor);
|
|
222
|
+
if (!policyJson)
|
|
223
|
+
continue;
|
|
224
|
+
const properties = policyJson.properties;
|
|
225
|
+
const policyContent = properties?.value;
|
|
226
|
+
if (policyContent) {
|
|
227
|
+
await store.writeContent(outputDir, opPolicyDescriptor, policyContent, 'policy');
|
|
228
|
+
operationPolicies.push({
|
|
229
|
+
descriptor: opPolicyDescriptor,
|
|
230
|
+
json: policyJson,
|
|
231
|
+
status: 'success',
|
|
232
|
+
});
|
|
233
|
+
policies.push(policyContent);
|
|
234
|
+
logger.debug(`Extracted ${buildResourceLabel(opPolicyDescriptor)}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { operations, operationPolicies, policies };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Extract API wiki content.
|
|
241
|
+
*/
|
|
242
|
+
async function extractApiWiki(client, store, context, apiDescriptor, outputDir) {
|
|
243
|
+
const wikiDescriptor = {
|
|
244
|
+
type: ResourceType.ApiWiki,
|
|
245
|
+
nameParts: [...apiDescriptor.nameParts],
|
|
246
|
+
workspace: apiDescriptor.workspace,
|
|
247
|
+
};
|
|
248
|
+
try {
|
|
249
|
+
const wikiJson = await client.getResource(context, wikiDescriptor);
|
|
250
|
+
if (!wikiJson) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
// Extract markdown content from wiki JSON
|
|
254
|
+
const properties = wikiJson.properties;
|
|
255
|
+
const documents = properties?.documents;
|
|
256
|
+
if (documents) {
|
|
257
|
+
// Write wiki info as markdown — use writeResource (not writeContent with 'policy')
|
|
258
|
+
// since there is no dedicated 'wiki' content type in IArtifactStore
|
|
259
|
+
const content = documents.map((d) => `# ${d.title}\n\n${d.documentationId}`).join('\n\n');
|
|
260
|
+
const markdownJson = { ...wikiJson, _markdownContent: content };
|
|
261
|
+
await store.writeResource(outputDir, wikiDescriptor, markdownJson);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// Write the raw JSON
|
|
265
|
+
await store.writeResource(outputDir, wikiDescriptor, wikiJson);
|
|
266
|
+
}
|
|
267
|
+
logger.info(`Extracted ${buildResourceLabel(wikiDescriptor)}`);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
272
|
+
logger.debug(`No wiki ${buildResourceLabel(wikiDescriptor)}: ${errorMessage}`);
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Extract GraphQL resolvers and their policies.
|
|
278
|
+
*/
|
|
279
|
+
async function extractGraphQLResolvers(client, store, context, apiDescriptor, apiJson, outputDir, filter, workspace) {
|
|
280
|
+
const resolvers = [];
|
|
281
|
+
const resolverPolicies = [];
|
|
282
|
+
const policies = [];
|
|
283
|
+
// Only extract resolvers for GraphQL APIs — use the already-fetched apiJson
|
|
284
|
+
const properties = apiJson.properties;
|
|
285
|
+
const apiType = properties?.type;
|
|
286
|
+
if (apiType?.toLowerCase() !== 'graphql') {
|
|
287
|
+
return { resolvers, resolverPolicies, policies };
|
|
288
|
+
}
|
|
289
|
+
// Extract resolvers
|
|
290
|
+
const resolverResult = await extractResourceType(client, store, context, ResourceType.GraphQLResolver, outputDir, filter, apiDescriptor, workspace);
|
|
291
|
+
resolvers.push(...resolverResult.extracted);
|
|
292
|
+
// Extract resolver policies
|
|
293
|
+
for (const resolver of resolverResult.extracted) {
|
|
294
|
+
if (resolver.status !== 'success')
|
|
295
|
+
continue;
|
|
296
|
+
const resolverPolicyDescriptor = {
|
|
297
|
+
type: ResourceType.GraphQLResolverPolicy,
|
|
298
|
+
nameParts: [...resolver.descriptor.nameParts],
|
|
299
|
+
workspace,
|
|
300
|
+
};
|
|
301
|
+
const policyJson = await client.getResource(context, resolverPolicyDescriptor);
|
|
302
|
+
if (!policyJson)
|
|
303
|
+
continue;
|
|
304
|
+
const props = policyJson.properties;
|
|
305
|
+
const policyContent = props?.value;
|
|
306
|
+
if (policyContent) {
|
|
307
|
+
await store.writeContent(outputDir, resolverPolicyDescriptor, policyContent, 'policy');
|
|
308
|
+
resolverPolicies.push({
|
|
309
|
+
descriptor: resolverPolicyDescriptor,
|
|
310
|
+
json: policyJson,
|
|
311
|
+
status: 'success',
|
|
312
|
+
});
|
|
313
|
+
policies.push(policyContent);
|
|
314
|
+
logger.debug(`Extracted ${buildResourceLabel(resolverPolicyDescriptor)}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return { resolvers, resolverPolicies, policies };
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=api-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-extractor.js","sourceRoot":"","sources":["../../src/services/api-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T032: API publisher with revision handling
|
|
3
|
+
* Create root API first, then revisions in numeric order with forced revision numbers.
|
|
4
|
+
* Publish operations, policies, schemas, releases, resolvers, tag descriptions, wikis per FR-024.
|
|
5
|
+
* Handle SOAP/WSDL import via ?import=true&format=wsdl-link query parameter.
|
|
6
|
+
*/
|
|
7
|
+
import type { IApimClient } from '../clients/iapim-client.js';
|
|
8
|
+
import type { IArtifactStore } from '../clients/iartifact-store.js';
|
|
9
|
+
import type { ApimServiceContext, ResourceDescriptor } from '../models/types.js';
|
|
10
|
+
import type { PublishConfig } from '../models/config.js';
|
|
11
|
+
import { type ResourcePublishResult } from './resource-publisher.js';
|
|
12
|
+
/**
|
|
13
|
+
* Publish an API with all its revisions and child resources.
|
|
14
|
+
* Creates root API first, then revisions in numeric order.
|
|
15
|
+
* Handles SOAP/WSDL import via ?import=true&format=wsdl-link.
|
|
16
|
+
*/
|
|
17
|
+
export declare function publishApi(client: IApimClient, store: IArtifactStore, context: ApimServiceContext, descriptor: ResourceDescriptor, config: PublishConfig): Promise<ResourcePublishResult>;
|
|
18
|
+
//# sourceMappingURL=api-publisher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-publisher.d.ts","sourceRoot":"","sources":["../../src/services/api-publisher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAwBtF;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,qBAAqB,CAAC,CA4BhC"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T032: API publisher with revision handling
|
|
3
|
+
* Create root API first, then revisions in numeric order with forced revision numbers.
|
|
4
|
+
* Publish operations, policies, schemas, releases, resolvers, tag descriptions, wikis per FR-024.
|
|
5
|
+
* Handle SOAP/WSDL import via ?import=true&format=wsdl-link query parameter.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceType } from '../models/resource-types.js';
|
|
8
|
+
import { publishResource } from './resource-publisher.js';
|
|
9
|
+
import { runParallel } from '../lib/parallel-runner.js';
|
|
10
|
+
import { applyOverrides } from './override-merger.js';
|
|
11
|
+
import { logger } from '../lib/logger.js';
|
|
12
|
+
import { getNamePart, getPublishTier } from '../lib/resource-path.js';
|
|
13
|
+
import { isAutoGeneratedId } from '../lib/auto-generated.js';
|
|
14
|
+
/**
|
|
15
|
+
* API child resource types that should be published after the API itself
|
|
16
|
+
*/
|
|
17
|
+
const API_CHILD_TYPES = [
|
|
18
|
+
ResourceType.ApiPolicy,
|
|
19
|
+
ResourceType.ApiTag,
|
|
20
|
+
ResourceType.ApiDiagnostic,
|
|
21
|
+
ResourceType.ApiOperation,
|
|
22
|
+
ResourceType.ApiOperationPolicy,
|
|
23
|
+
ResourceType.ApiSchema,
|
|
24
|
+
ResourceType.ApiRelease,
|
|
25
|
+
ResourceType.ApiTagDescription,
|
|
26
|
+
ResourceType.ApiWiki,
|
|
27
|
+
ResourceType.GraphQLResolver,
|
|
28
|
+
ResourceType.GraphQLResolverPolicy,
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Publish an API with all its revisions and child resources.
|
|
32
|
+
* Creates root API first, then revisions in numeric order.
|
|
33
|
+
* Handles SOAP/WSDL import via ?import=true&format=wsdl-link.
|
|
34
|
+
*/
|
|
35
|
+
export async function publishApi(client, store, context, descriptor, config) {
|
|
36
|
+
try {
|
|
37
|
+
// Step 1: Publish root API (with spec import if available)
|
|
38
|
+
const rootResult = await publishRootApi(client, store, context, descriptor, config);
|
|
39
|
+
if (rootResult.status !== 'success') {
|
|
40
|
+
return rootResult;
|
|
41
|
+
}
|
|
42
|
+
// Step 2: Find and publish revisions in numeric order
|
|
43
|
+
await publishApiRevisions(client, store, context, descriptor, config);
|
|
44
|
+
// Step 3: Publish child resources in parallel
|
|
45
|
+
// When a spec was imported, operations and schemas are auto-created by APIM
|
|
46
|
+
await publishApiChildren(client, store, context, descriptor, config, rootResult.specImported);
|
|
47
|
+
return {
|
|
48
|
+
descriptor,
|
|
49
|
+
status: 'success',
|
|
50
|
+
action: 'put',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
descriptor,
|
|
56
|
+
status: 'failed',
|
|
57
|
+
action: 'noop',
|
|
58
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Maps spec file format to APIM ContentFormat for inline import.
|
|
64
|
+
*/
|
|
65
|
+
function getImportFormat(specFormat, _apiType) {
|
|
66
|
+
switch (specFormat) {
|
|
67
|
+
case 'yaml':
|
|
68
|
+
return 'openapi';
|
|
69
|
+
case 'json':
|
|
70
|
+
// Swagger 2.0 uses swagger-json, OpenAPI 3.x uses openapi+json.
|
|
71
|
+
// Default to openapi+json as the more modern format — APIM accepts both.
|
|
72
|
+
return 'openapi+json';
|
|
73
|
+
case 'wsdl':
|
|
74
|
+
return 'wsdl';
|
|
75
|
+
case 'wadl':
|
|
76
|
+
return 'wadl-xml';
|
|
77
|
+
case 'graphql':
|
|
78
|
+
// GraphQL schemas are managed via the ApiSchema resource, not via
|
|
79
|
+
// the import properties. Return undefined so we skip injection.
|
|
80
|
+
return undefined;
|
|
81
|
+
default:
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Publish the root API (apiInformation.json).
|
|
87
|
+
* When a specification file exists alongside the API metadata, injects
|
|
88
|
+
* `properties.format` and `properties.value` into the PUT body so APIM
|
|
89
|
+
* imports the spec and auto-creates operations + schemas.
|
|
90
|
+
*/
|
|
91
|
+
async function publishRootApi(client, store, context, descriptor, config) {
|
|
92
|
+
let json = await store.readResource(config.sourceDir, descriptor);
|
|
93
|
+
if (!json) {
|
|
94
|
+
return {
|
|
95
|
+
descriptor,
|
|
96
|
+
status: 'skipped',
|
|
97
|
+
action: 'noop',
|
|
98
|
+
specImported: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Apply overrides
|
|
102
|
+
json = applyOverrides(descriptor, json, config.overrides);
|
|
103
|
+
// Try to read the specification file for this API
|
|
104
|
+
let specImported = false;
|
|
105
|
+
const specResult = await store.readContent(config.sourceDir, descriptor, 'specification');
|
|
106
|
+
if (specResult) {
|
|
107
|
+
const properties = json.properties;
|
|
108
|
+
const apiType = properties?.type;
|
|
109
|
+
const importFormat = getImportFormat(specResult.format ?? 'yaml', apiType);
|
|
110
|
+
if (importFormat) {
|
|
111
|
+
// Strip null-valued properties that cause validation errors in
|
|
112
|
+
// APIM's spec-import mode (e.g. "backendId": null → HTTP 400).
|
|
113
|
+
const cleanProps = {};
|
|
114
|
+
for (const [key, val] of Object.entries(properties ?? {})) {
|
|
115
|
+
if (val !== null) {
|
|
116
|
+
cleanProps[key] = val;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// The `type` property from GET is read-only and ignored on PUT.
|
|
120
|
+
// APIM uses `apiType` to determine the API kind during spec import.
|
|
121
|
+
// Always set it explicitly (valid values: http, soap, websocket, graphql, odata, grpc, mcp).
|
|
122
|
+
if (apiType) {
|
|
123
|
+
cleanProps.apiType = apiType;
|
|
124
|
+
}
|
|
125
|
+
// Inject the spec into the PUT body for APIM to import
|
|
126
|
+
json = {
|
|
127
|
+
...json,
|
|
128
|
+
properties: {
|
|
129
|
+
...cleanProps,
|
|
130
|
+
format: importFormat,
|
|
131
|
+
value: specResult.content,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
specImported = true;
|
|
135
|
+
logger.info(`Including ${specResult.format} specification in API import for "${getNamePart(descriptor.nameParts, 0)}"`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// PUT the API resource to APIM
|
|
139
|
+
await client.putResource(context, descriptor, json);
|
|
140
|
+
return {
|
|
141
|
+
descriptor,
|
|
142
|
+
status: 'success',
|
|
143
|
+
action: 'put',
|
|
144
|
+
specImported,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Find and publish API revisions in numeric order
|
|
149
|
+
*/
|
|
150
|
+
async function publishApiRevisions(client, store, context, apiDescriptor, config) {
|
|
151
|
+
// List all resources from store
|
|
152
|
+
const allDescriptors = await store.listResources(config.sourceDir);
|
|
153
|
+
// Find revision descriptors for this API
|
|
154
|
+
const revisionDescriptors = allDescriptors.filter((d) => d.type === ResourceType.Api &&
|
|
155
|
+
getNamePart(d.nameParts, 0).startsWith(`${getNamePart(apiDescriptor.nameParts, 0)};rev=`));
|
|
156
|
+
// Sort revisions by revision number
|
|
157
|
+
const sortedRevisions = revisionDescriptors.sort((a, b) => {
|
|
158
|
+
const revA = extractRevisionNumber(getNamePart(a.nameParts, 0));
|
|
159
|
+
const revB = extractRevisionNumber(getNamePart(b.nameParts, 0));
|
|
160
|
+
return revA - revB;
|
|
161
|
+
});
|
|
162
|
+
// Publish each revision in order
|
|
163
|
+
for (const revDescriptor of sortedRevisions) {
|
|
164
|
+
await publishResource(client, store, context, revDescriptor, config);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Resource types that are auto-created by APIM when importing a spec.
|
|
169
|
+
* Both ApiOperation and ApiSchema are initially excluded, but we selectively
|
|
170
|
+
* re-add: operations with schema references, and explicitly named schemas
|
|
171
|
+
* (non-auto-generated 24-char hex IDs).
|
|
172
|
+
*/
|
|
173
|
+
const SPEC_MANAGED_CHILD_TYPES = new Set([
|
|
174
|
+
ResourceType.ApiOperation,
|
|
175
|
+
ResourceType.ApiSchema,
|
|
176
|
+
]);
|
|
177
|
+
/**
|
|
178
|
+
* Publish all child resources of an API in parallel.
|
|
179
|
+
* When specImported is true, skips ApiSchema and ApiOperation since APIM
|
|
180
|
+
* auto-creates those from the imported specification. However:
|
|
181
|
+
* - Operations with schema references are re-PUT explicitly so APIM preserves those links
|
|
182
|
+
* - Explicitly named schemas (non-24-char-hex IDs) are also re-published
|
|
183
|
+
*/
|
|
184
|
+
async function publishApiChildren(client, store, context, apiDescriptor, config, specImported = false) {
|
|
185
|
+
// List all resources from store
|
|
186
|
+
const allDescriptors = await store.listResources(config.sourceDir);
|
|
187
|
+
// Find child descriptors for this API
|
|
188
|
+
let childDescriptors = allDescriptors.filter((d) => API_CHILD_TYPES.includes(d.type) &&
|
|
189
|
+
getNamePart(d.nameParts, 0) === getNamePart(apiDescriptor.nameParts, 0) &&
|
|
190
|
+
!(specImported && SPEC_MANAGED_CHILD_TYPES.has(d.type)));
|
|
191
|
+
// When a spec was imported, APIM auto-creates operations and schemas but loses
|
|
192
|
+
// schemaId/typeName references in representations. Re-include operations that
|
|
193
|
+
// carry such references so those links are explicitly restored via PUT.
|
|
194
|
+
if (specImported) {
|
|
195
|
+
const operationDescriptors = allDescriptors.filter((d) => d.type === ResourceType.ApiOperation &&
|
|
196
|
+
getNamePart(d.nameParts, 0) === getNamePart(apiDescriptor.nameParts, 0));
|
|
197
|
+
const schemaRefOps = await filterOperationsWithSchemaRefs(store, config.sourceDir, operationDescriptors);
|
|
198
|
+
if (schemaRefOps.length > 0) {
|
|
199
|
+
logger.debug(`Re-publishing ${schemaRefOps.length} operation(s) with schema references after spec import for "${getNamePart(apiDescriptor.nameParts, 0)}"`);
|
|
200
|
+
childDescriptors = [...childDescriptors, ...schemaRefOps];
|
|
201
|
+
}
|
|
202
|
+
// Re-include explicitly named schemas (non-auto-generated IDs).
|
|
203
|
+
// Auto-generated schemas have 24-char hex names and are recreated by spec import.
|
|
204
|
+
// Explicitly named schemas (like "src-rest-schema-item") must be published.
|
|
205
|
+
const explicitSchemas = allDescriptors.filter((d) => d.type === ResourceType.ApiSchema &&
|
|
206
|
+
getNamePart(d.nameParts, 0) === getNamePart(apiDescriptor.nameParts, 0) &&
|
|
207
|
+
!isAutoGeneratedId(getNamePart(d.nameParts, 1)));
|
|
208
|
+
if (explicitSchemas.length > 0) {
|
|
209
|
+
logger.debug(`Re-publishing ${explicitSchemas.length} explicit schema(s) after spec import for "${getNamePart(apiDescriptor.nameParts, 0)}"`);
|
|
210
|
+
childDescriptors = [...childDescriptors, ...explicitSchemas];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Group resources by publish tier for dependency ordering.
|
|
214
|
+
// Lower tiers are published first (parents before children, operations before policies).
|
|
215
|
+
// Resources in the same tier can be published in parallel.
|
|
216
|
+
const tierMap = new Map();
|
|
217
|
+
for (const d of childDescriptors) {
|
|
218
|
+
const tier = getPublishTier(d.type);
|
|
219
|
+
const existing = tierMap.get(tier) ?? [];
|
|
220
|
+
existing.push(d);
|
|
221
|
+
tierMap.set(tier, existing);
|
|
222
|
+
}
|
|
223
|
+
// Publish each tier in order (lowest to highest)
|
|
224
|
+
const sortedTiers = [...tierMap.keys()].sort((a, b) => a - b);
|
|
225
|
+
for (const tier of sortedTiers) {
|
|
226
|
+
const descriptors = tierMap.get(tier) ?? [];
|
|
227
|
+
if (descriptors.length > 0) {
|
|
228
|
+
const tasks = descriptors.map((descriptor) => () => publishResource(client, store, context, descriptor, config));
|
|
229
|
+
await runParallel(tasks, 5);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Returns the subset of operation descriptors whose stored artifact contains
|
|
235
|
+
* at least one representation with a schemaId or typeName property.
|
|
236
|
+
* These operations must be re-PUT after a spec import so APIM retains the
|
|
237
|
+
* schema linkage that the import path discards.
|
|
238
|
+
*/
|
|
239
|
+
async function filterOperationsWithSchemaRefs(store, sourceDir, operationDescriptors) {
|
|
240
|
+
const result = [];
|
|
241
|
+
for (const descriptor of operationDescriptors) {
|
|
242
|
+
const json = await store.readResource(sourceDir, descriptor);
|
|
243
|
+
if (json && operationHasSchemaRefs(json)) {
|
|
244
|
+
result.push(descriptor);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Returns true if the operation JSON contains at least one representation
|
|
251
|
+
* (in request or any response) that has a schemaId or typeName property.
|
|
252
|
+
*/
|
|
253
|
+
function operationHasSchemaRefs(json) {
|
|
254
|
+
const props = json.properties;
|
|
255
|
+
if (!props)
|
|
256
|
+
return false;
|
|
257
|
+
const representationGroups = [];
|
|
258
|
+
const request = props.request;
|
|
259
|
+
if (request?.representations) {
|
|
260
|
+
representationGroups.push(request.representations);
|
|
261
|
+
}
|
|
262
|
+
const responses = props.responses;
|
|
263
|
+
if (Array.isArray(responses)) {
|
|
264
|
+
for (const response of responses) {
|
|
265
|
+
const r = response;
|
|
266
|
+
if (r.representations) {
|
|
267
|
+
representationGroups.push(r.representations);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
for (const group of representationGroups) {
|
|
272
|
+
if (!Array.isArray(group))
|
|
273
|
+
continue;
|
|
274
|
+
for (const rep of group) {
|
|
275
|
+
const r = rep;
|
|
276
|
+
if (r.schemaId != null || r.typeName != null) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Extract revision number from API name (e.g., "my-api;rev=2" -> 2)
|
|
285
|
+
*/
|
|
286
|
+
function extractRevisionNumber(apiName) {
|
|
287
|
+
const match = /;rev=(\d+)/.exec(apiName);
|
|
288
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=api-publisher.js.map
|