@@ -11,6 +11,7 @@ import { type AppResource, type AppResourceResolver } from "@microsoft/vscode-az
1111import { ResolvedFunctionAppResource } from "./tree/ResolvedFunctionAppResource" ;
1212import { ResolvedContainerizedFunctionAppResource } from "./tree/containerizedFunctionApp/ResolvedContainerizedFunctionAppResource" ;
1313import { createResourceGraphClient } from "./utils/azureClients" ;
14+ import { getGlobalSetting } from "./vsCodeConfig/settings" ;
1415
1516export type FunctionAppModel = {
1617 isFlex : boolean ,
@@ -39,6 +40,7 @@ export class FunctionAppResolver implements AppResourceResolver {
3940 private loaded : boolean = false ;
4041 private siteCacheLastUpdated = 0 ;
4142 private siteCache : Map < string , FunctionAppModel > = new Map < string , FunctionAppModel > ( ) ;
43+ private siteNameCounter : Map < string , number > = new Map < string , number > ( ) ;
4244 private listFunctionAppsTask : Promise < void > | undefined ;
4345
4446 public async resolveResource ( subContext : ISubscriptionContext , resource : AppResource ) : Promise < ResolvedFunctionAppResource | ResolvedContainerizedFunctionAppResource | undefined > {
@@ -48,8 +50,10 @@ export class FunctionAppResolver implements AppResourceResolver {
4850 // do this before the graph client is created because the async graph client create takes enough time to mess up the following resolves
4951 this . loaded = false ;
5052 this . siteCache . clear ( ) ; // clear the cache before fetching new data
53+ this . siteNameCounter . clear ( ) ;
5154 this . siteCacheLastUpdated = Date . now ( ) ;
5255 const graphClient = await createResourceGraphClient ( { ...context , ...subContext } ) ;
56+
5357 async function fetchAllApps ( graphClient : ResourceGraphClient , subContext : ISubscriptionContext , resolver : FunctionAppResolver ) : Promise < void > {
5458 const query = `resources | where type == 'microsoft.web/sites' and kind contains 'functionapp' and kind !contains 'workflowapp'` ;
5559
@@ -65,6 +69,7 @@ export class FunctionAppResolver implements AppResourceResolver {
6569 const record = response . data as Record < string , FunctionQueryModel > ;
6670 // seems as if properties can be null, so we need to check for that
6771 Object . values ( record ) . forEach ( data => {
72+ resolver . countSiteName ( data . name ) ;
6873 const dataModel : FunctionAppModel = {
6974 isFlex : data . properties ?. sku ?. toLocaleLowerCase ( ) === 'flexconsumption' ,
7075 id : data . id ,
@@ -98,18 +103,36 @@ export class FunctionAppResolver implements AppResourceResolver {
98103 await this . listFunctionAppsTask ;
99104 }
100105
106+ const groupBy : string | undefined = getGlobalSetting < string > ( 'groupBy' , 'azureResourceGroups' ) ;
107+ const hasDuplicateSiteName : boolean = ( this . siteNameCounter . get ( resource . name ) ?? 1 ) > 1 ;
108+
101109 const siteModel = this . siteCache . get ( nonNullProp ( resource , 'id' ) . toLowerCase ( ) ) ;
102110 if ( ! siteModel || siteModel . kind === 'functionapp,linux,container,azurecontainerapps' ) {
111+ // Edge Case: `siteModel` can be `undefined` after calling `createFunctionApp` because the result does not propagate to MS Graph in time; a fallback fetch + count is needed
112+ if ( ! siteModel ) {
113+ this . countSiteName ( resource . name ) ;
114+ }
115+
103116 // if the site model is not found or if it's a containerized function app, we need the full site details
104117 const client = await createWebSiteClient ( { ...context , ...subContext } ) ;
105118 const fullSite = await client . webApps . get ( getResourceGroupFromId ( resource . id ) , resource . name ) ;
119+
106120 if ( fullSite . kind === 'functionapp,linux,container,azurecontainerapps' ) {
107- return ResolvedContainerizedFunctionAppResource . createResolvedFunctionAppResource ( context , subContext , fullSite ) ;
121+ return ResolvedContainerizedFunctionAppResource . createResolvedFunctionAppResource ( context , subContext , fullSite , {
122+ // Display the location as well if there is a duplicate name, otherwise they won't be easily distinguishable in the tree view
123+ showLocationInTreeItemDescription : groupBy === 'resourceType' && hasDuplicateSiteName ,
124+ } ) ;
108125 }
109126
110- return new ResolvedFunctionAppResource ( subContext , fullSite ) ;
127+ return new ResolvedFunctionAppResource ( subContext , fullSite , undefined , {
128+ // Display the location as well if there is a duplicate name, otherwise they won't be easily distinguishable in the tree view
129+ showLocationInTreeItemDescription : groupBy === 'resourceType' && hasDuplicateSiteName ,
130+ } ) ;
111131 } else if ( siteModel ) {
112- return new ResolvedFunctionAppResource ( subContext , undefined , siteModel ) ;
132+ return new ResolvedFunctionAppResource ( subContext , undefined , siteModel , {
133+ // Display the location as well if there is a duplicate name, otherwise they won't be easily distinguishable in the tree view
134+ showLocationInTreeItemDescription : groupBy === 'resourceType' && hasDuplicateSiteName ,
135+ } ) ;
113136 }
114137
115138 return undefined ;
@@ -121,4 +144,9 @@ export class FunctionAppResolver implements AppResourceResolver {
121144 && ! ! resource . kind ?. includes ( 'functionapp' )
122145 && ! resource . kind ?. includes ( 'workflowapp' ) ; // exclude logic apps
123146 }
147+
148+ private countSiteName ( siteName : string ) : void {
149+ const count : number = ( this . siteNameCounter . get ( siteName ) ?? 0 ) + 1 ;
150+ this . siteNameCounter . set ( siteName , count ) ;
151+ }
124152}
0 commit comments