@@ -15,8 +15,10 @@ import * as yaml from 'js-yaml';
1515import { ResourceType } from '../models/resource-types.js' ;
1616import {
1717 applyApiPathPrefix ,
18+ normalizeApiAuthenticationSettings ,
1819 normalizeMcpToolOperationIds ,
1920 normalizeApiVersionSetId ,
21+ prefersLegacyAuthOverride ,
2022 publishResource ,
2123 type ResourcePublishResult ,
2224} from './resource-publisher.js' ;
@@ -77,6 +79,7 @@ export async function publishApi(
7779 allowedDescriptors ?: ResourceDescriptor [ ]
7880) : Promise < ResourcePublishResult > {
7981 let publicationPlan : ApiPublicationPlan ;
82+ let rootPutDescriptor : ResourceDescriptor ;
8083 let rootPublished = false ;
8184 try {
8285 publicationPlan = await planApiPublication (
@@ -85,6 +88,20 @@ export async function publishApi(
8588 config ,
8689 allowedDescriptors
8790 ) ;
91+
92+ // Deployed (env-mapped) base descriptor — derived once and used for every
93+ // direct APIM call so canonical and affixed names can never diverge.
94+ const deployedDescriptor = config . envMapping
95+ ? mapDescriptor ( descriptor , config . envMapping )
96+ : descriptor ;
97+ rootPutDescriptor = await resolveRootApiPutDescriptor (
98+ client ,
99+ store ,
100+ context ,
101+ descriptor ,
102+ deployedDescriptor ,
103+ config
104+ ) ;
88105 } catch ( error ) {
89106 return {
90107 descriptor,
@@ -98,6 +115,7 @@ export async function publishApi(
98115 // Step 1: Publish root API (with spec import if available)
99116 const rootResult = await publishRootApi ( client , store , context , descriptor , config , {
100117 includeSpecification : publicationPlan . importSpecification ,
118+ putDescriptor : rootPutDescriptor ,
101119 } ) ;
102120 if ( rootResult . status !== 'success' ) {
103121 return rootResult ;
@@ -213,7 +231,7 @@ export async function planApiPublication(
213231 ? new Set ( allowedDescriptors . map ( getResourceDescriptorKey ) )
214232 : undefined ;
215233 const rootName = apiName . toLowerCase ( ) ;
216- const revisions = allDescriptors
234+ let revisions = allDescriptors
217235 . filter (
218236 ( descriptor ) =>
219237 descriptor . type === ResourceType . Api &&
@@ -234,6 +252,14 @@ export async function planApiPublication(
234252 const mergedApiJson = apiJson
235253 ? applyOverrides ( apiDescriptor , apiJson , config . overrides )
236254 : undefined ;
255+ const rootRevision = ( apiJson ?. properties as Record < string , unknown > | undefined ) ?. apiRevision ;
256+ if ( typeof rootRevision === 'string' && rootRevision !== '' ) {
257+ const rootRevisionNumber = Number ( rootRevision ) ;
258+ revisions = revisions . filter (
259+ ( revision ) =>
260+ extractRevisionNumber ( getNamePart ( revision . nameParts , 0 ) ) !== rootRevisionNumber
261+ ) ;
262+ }
237263 const specificationAllowed =
238264 subFilter ?. operations === undefined &&
239265 subFilter ?. schemas === undefined &&
@@ -387,6 +413,49 @@ interface RootApiResult {
387413
388414interface PublishRootApiOptions {
389415 includeSpecification ?: boolean ;
416+ /** Descriptor to PUT to (defaults to the artifact descriptor). Lets the root
417+ * API be created at apis/{name};rev=N while still reading apis/{name} artifacts. */
418+ putDescriptor ?: ResourceDescriptor ;
419+ }
420+
421+ /**
422+ * A plain root PUT creates a brand-new API as revision 1, which collides with
423+ * a ;rev=1 revision artifact and silently absorbs it whenever the source's
424+ * current revision number is > 1. When the API does not yet exist on the
425+ * target, PUT the root at its true revision number (apis/{name};rev=N) instead.
426+ * Existing APIs keep the plain root PUT — their current revision cannot be
427+ * renumbered.
428+ *
429+ * Reads artifacts via the canonical descriptor; all APIM lookups and the
430+ * returned PUT target use the deployed (env-mapped) descriptor, with ;rev=N
431+ * appended after affixing so suffix mappings cannot corrupt it.
432+ */
433+ async function resolveRootApiPutDescriptor (
434+ client : IApimClient ,
435+ store : IArtifactStore ,
436+ context : ApimServiceContext ,
437+ descriptor : ResourceDescriptor ,
438+ deployedDescriptor : ResourceDescriptor ,
439+ config : PublishConfig
440+ ) : Promise < ResourceDescriptor > {
441+ const json = await store . readResource ( config . sourceDir , descriptor ) ;
442+ const rev = ( json ?. properties as Record < string , unknown > | undefined ) ?. apiRevision ;
443+ if ( typeof rev !== 'string' || rev === '' || rev === '1' ) {
444+ return deployedDescriptor ;
445+ }
446+
447+ const existing = await client . getResource ( context , deployedDescriptor ) ;
448+ if ( existing ) {
449+ return deployedDescriptor ;
450+ }
451+
452+ return {
453+ ...deployedDescriptor ,
454+ nameParts : [
455+ `${ getNamePart ( deployedDescriptor . nameParts , 0 ) } ;rev=${ rev } ` ,
456+ ...deployedDescriptor . nameParts . slice ( 1 ) ,
457+ ] ,
458+ } ;
390459}
391460
392461/**
@@ -420,6 +489,12 @@ async function publishRootApi(
420489
421490 // Apply overrides
422491 json = applyOverrides ( descriptor , json , config . overrides ) ;
492+ json = normalizeApiAuthenticationSettings ( json , {
493+ preferLegacyFields : prefersLegacyAuthOverride (
494+ getNamePart ( descriptor . nameParts , 0 ) ,
495+ config . overrides ?. apis
496+ ) ,
497+ } ) ;
423498 const isCurrent = getApiIsCurrent ( json ) ;
424499
425500 // Try to read the specification file for this API
@@ -482,7 +557,7 @@ async function publishRootApi(
482557
483558 // PUT the API resource to APIM
484559 try {
485- await client . putResource ( context , deployedDescriptor , json ) ;
560+ await client . putResource ( context , options ?. putDescriptor ?? deployedDescriptor , json ) ;
486561 } catch ( error ) {
487562 throw new ApiPutAttemptError ( error ) ;
488563 }
@@ -525,7 +600,13 @@ async function publishApiRevisions(
525600 // Publish each revision in order
526601 const results : ResourcePublishResult [ ] = [ ] ;
527602 for ( const revDescriptor of revisionDescriptors ) {
528- results . push ( await publishResource ( client , store , context , revDescriptor , config ) ) ;
603+ const result = await publishResource ( client , store , context , revDescriptor , config ) ;
604+ if ( result . status === 'failed' ) {
605+ throw new Error (
606+ `Failed to publish revision ${ getNamePart ( revDescriptor . nameParts , 0 ) } : ${ result . error ?. message ?? 'unknown error' } `
607+ ) ;
608+ }
609+ results . push ( result ) ;
529610 }
530611
531612 return results ;
0 commit comments