Skip to content

Commit 03612c6

Browse files
authored
Merge pull request #272 from Alexey-Zheltov/fix/wsdl-xsd-schemas-and-dryrun-label
Fix/wsdl xsd schemas and dryrun label
2 parents 5e1dd93 + 22a966d commit 03612c6

4 files changed

Lines changed: 121 additions & 3 deletions

File tree

‎src/lib/resource-uri.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ export function parseArmUri(
130130
*/
131131
export function buildResourceLabel(descriptor: ResourceDescriptor): string {
132132
const metadata = RESOURCE_TYPE_METADATA[descriptor.type];
133+
134+
// Association types (e.g. GatewayApi) are sometimes discovered at the
135+
// parent-association-file level — a descriptor carrying only the parent's
136+
// name-parts (e.g. GatewayApi ['my-gateway'] representing
137+
// gateways/my-gateway/apis.json before per-API entries are expanded).
138+
// armPathSuffix has more placeholders than such a descriptor can fill, so
139+
// fall back to artifactDirectory (which only needs the parent's parts).
140+
if (descriptor.nameParts.length < countTemplatePlaceholders(metadata.armPathSuffix)) {
141+
return formatTemplatePath(metadata.artifactDirectory, descriptor.nameParts);
142+
}
143+
133144
// armPathSuffix has no leading slash, so the result is already relative
134145
return formatTemplatePath(metadata.armPathSuffix, descriptor.nameParts);
135146
}

‎src/services/api-publisher.ts‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ export async function planApiPublication(
276276
);
277277

278278
let importSpecification = false;
279+
let importSpecificationFormat: string | undefined;
279280
let importSpecSchemaComponents: Record<string, unknown> | undefined;
280281
let operationDescriptionPuts: ResourceDescriptor[] = [];
281282
if (specificationAllowed) {
@@ -286,6 +287,7 @@ export async function planApiPublication(
286287
const dialect = detectSpecDialect(specification.content, specification.format);
287288
importSpecification =
288289
getImportFormat(specification.format ?? 'yaml', apiType, dialect) !== undefined;
290+
importSpecificationFormat = importSpecification ? specification.format : undefined;
289291
if (importSpecification) {
290292
importSpecSchemaComponents = getSpecComponentSchemas(
291293
specification.content,
@@ -365,15 +367,31 @@ export async function planApiPublication(
365367
})
366368
)
367369
).filter((descriptor): descriptor is ResourceDescriptor => descriptor !== undefined);
370+
// WSDL-import-generated XSD schemas have GUID names (not 24-hex), so the
371+
// isAutoGeneratedId filter misses them. APIM recreates them on WSDL import
372+
// and rejects re-PUTs, so exclude schemas with the XSD content type — but
373+
// only for WSDL imports; other importers do not recreate XSD schemas.
374+
const nonImporterSchemas = importSpecificationFormat !== 'wsdl'
375+
? explicitSchemas
376+
: (
377+
await Promise.all(
378+
explicitSchemas.map(async (descriptor) => {
379+
const schemaJson = await store.readResource(config.sourceDir, descriptor);
380+
const contentType = (schemaJson?.properties as Record<string, unknown> | undefined)
381+
?.contentType;
382+
return contentType === 'application/vnd.ms-azure-apim.xsd+xml' ? undefined : descriptor;
383+
})
384+
)
385+
).filter((descriptor): descriptor is ResourceDescriptor => descriptor !== undefined);
368386
const filteredExplicitSchemas = config.filter
369-
? explicitSchemas.filter((descriptor) =>
387+
? nonImporterSchemas.filter((descriptor) =>
370388
shouldIncludeResource(descriptor, effectiveFilter)
371389
)
372390
: allowed
373-
? explicitSchemas.filter((descriptor) =>
391+
? nonImporterSchemas.filter((descriptor) =>
374392
allowed.has(getResourceDescriptorKey(descriptor))
375393
)
376-
: explicitSchemas;
394+
: nonImporterSchemas;
377395
childPuts = [...childPuts, ...filteredExplicitSchemas];
378396
}
379397

‎tests/unit/lib/resource-uri.test.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,26 @@ describe('buildResourceLabel', () => {
251251
expect(label).toBe('apis/petstore/operations/get-user');
252252
});
253253

254+
it('should fall back to artifact directory for parent-level association descriptor (GatewayApi)', () => {
255+
// GatewayApi discovered from gateways/{gateway}/apis.json carries only the
256+
// gateway name; armPathSuffix needs two parts and used to throw fatally.
257+
const descriptor: ResourceDescriptor = {
258+
type: ResourceType.GatewayApi,
259+
nameParts: ['my-gateway'],
260+
};
261+
const label = buildResourceLabel(descriptor);
262+
expect(label).toBe('gateways/my-gateway');
263+
});
264+
265+
it('should format fully-expanded association descriptor (GatewayApi)', () => {
266+
const descriptor: ResourceDescriptor = {
267+
type: ResourceType.GatewayApi,
268+
nameParts: ['my-gateway', 'my-api'],
269+
};
270+
const label = buildResourceLabel(descriptor);
271+
expect(label).toBe('gateways/my-gateway/apis/my-api');
272+
});
273+
254274
it('should format grandchild policy resource (ApiOperationPolicy)', () => {
255275
const descriptor: ResourceDescriptor = {
256276
type: ResourceType.ApiOperationPolicy,

‎tests/unit/services/api-publisher.test.ts‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,75 @@ describe('api-publisher', () => {
18941894
expect(totalTasks).toBe(1);
18951895
});
18961896

1897+
it('should skip WSDL-importer-generated XSD schemas (GUID names) on WSDL import', async () => {
1898+
const client = createMockClient();
1899+
// GUID name is not caught by isAutoGeneratedId (24-hex only), but the
1900+
// WSDL importer recreates XSD schemas, so re-PUTs must be excluded.
1901+
const xsdSchema = {
1902+
type: ResourceType.ApiSchema,
1903+
nameParts: ['soap-api', '4b6fe4d6-dbad-4db0-a6d6-58958e74850d'],
1904+
};
1905+
const store = createMockStore([xsdSchema]);
1906+
store.readResource.mockImplementation(async (_dir: string, descriptor: ResourceDescriptor) => {
1907+
if (descriptor.type === ResourceType.Api) {
1908+
return { name: 'soap-api', properties: { path: 'soap', type: 'soap' } };
1909+
}
1910+
if (descriptor.type === ResourceType.ApiSchema) {
1911+
return {
1912+
name: descriptor.nameParts[1],
1913+
properties: { contentType: 'application/vnd.ms-azure-apim.xsd+xml', document: { value: '<xsd:schema/>' } },
1914+
};
1915+
}
1916+
return null;
1917+
});
1918+
store.readContent.mockResolvedValue({
1919+
content: '<wsdl:definitions>...</wsdl:definitions>',
1920+
format: 'wsdl',
1921+
});
1922+
1923+
const apiDescriptor: ResourceDescriptor = { type: ResourceType.Api, nameParts: ['soap-api'] };
1924+
await publishApi(client, store, testContext, apiDescriptor, testConfig);
1925+
1926+
const totalTasks = mockRunParallel.mock.calls.reduce((sum, call) => {
1927+
const tasks = call[0] as unknown[];
1928+
return sum + tasks.length;
1929+
}, 0);
1930+
expect(totalTasks).toBe(0);
1931+
});
1932+
1933+
it('should retain GUID-named XSD schemas for non-WSDL spec imports', async () => {
1934+
const client = createMockClient();
1935+
// OpenAPI import does not recreate XSD schemas, so the exclusion must
1936+
// apply only when the imported spec format is wsdl.
1937+
const xsdSchema = {
1938+
type: ResourceType.ApiSchema,
1939+
nameParts: ['rest-api', '4b6fe4d6-dbad-4db0-a6d6-58958e74850d'],
1940+
};
1941+
const store = createMockStore([xsdSchema]);
1942+
store.readResource.mockImplementation(async (_dir: string, descriptor: ResourceDescriptor) => {
1943+
if (descriptor.type === ResourceType.Api) {
1944+
return { name: 'rest-api', properties: { path: 'rest' } };
1945+
}
1946+
if (descriptor.type === ResourceType.ApiSchema) {
1947+
return {
1948+
name: descriptor.nameParts[1],
1949+
properties: { contentType: 'application/vnd.ms-azure-apim.xsd+xml', document: { value: '<xsd:schema/>' } },
1950+
};
1951+
}
1952+
return null;
1953+
});
1954+
store.readContent.mockResolvedValue({ content: 'openapi: "3.0.0"', format: 'yaml' });
1955+
1956+
const apiDescriptor: ResourceDescriptor = { type: ResourceType.Api, nameParts: ['rest-api'] };
1957+
await publishApi(client, store, testContext, apiDescriptor, testConfig);
1958+
1959+
const totalTasks = mockRunParallel.mock.calls.reduce((sum, call) => {
1960+
const tasks = call[0] as unknown[];
1961+
return sum + tasks.length;
1962+
}, 0);
1963+
expect(totalTasks).toBe(1);
1964+
});
1965+
18971966
it('should skip 13-digit portal schema when the imported spec recreates its components', async () => {
18981967
const client = createMockClient();
18991968
// The portal assigns Date.now()-style schema IDs; when the imported spec

0 commit comments

Comments
 (0)