Skip to content

Commit

Permalink
Prefix all ids with account and tenant ids (#991)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexweininger authored Dec 19, 2024
1 parent 8425d64 commit 7b2ffa7
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 22 deletions.
16 changes: 4 additions & 12 deletions src/tree/BranchDataItemCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { isAzExtTreeItem } from '@microsoft/vscode-azext-utils';
import { ResourceModelBase } from 'api/src/resources/base';
import { BranchDataItemWrapper } from './BranchDataItemWrapper';
import { ResourceGroupsItem } from './ResourceGroupsItem';
Expand All @@ -29,17 +28,14 @@ export class BranchDataItemCache {
return this.branchItemToResourceGroupsItemCache.get(branchItem);
}

getItemForBranchItemById(branchItem: ResourceModelBase): ResourceGroupsItem | undefined {
const id = this.getIdForBranchItem(branchItem);
if (!id) {
return undefined;
}
getItemForBranchItemById(id: string): ResourceGroupsItem | undefined {
const cachedBranchItem = this.idToBranchItemCache.get(id);
return cachedBranchItem ? this.branchItemToResourceGroupsItemCache.get(cachedBranchItem) : undefined;
}

createOrGetItem<T extends BranchDataItemWrapper>(branchItem: ResourceModelBase, createItem: () => T): T {
const cachedItem = this.getItemForBranchItemById(branchItem) as T | undefined;
createOrGetItem<T extends BranchDataItemWrapper>(branchItem: ResourceModelBase, createItem: () => T, id: string): T {
const itemId = id ?? this.getIdForBranchItem(branchItem);
const cachedItem = this.getItemForBranchItemById(itemId) as T | undefined;
if (cachedItem) {
cachedItem.branchItem = branchItem;
this.addBranchItem(branchItem, cachedItem);
Expand All @@ -49,10 +45,6 @@ export class BranchDataItemCache {
}

private getIdForBranchItem(branchItem: ResourceModelBase): string | undefined {
if (isAzExtTreeItem(branchItem)) {
return branchItem.fullId;
}

return branchItem.id;
}
}
9 changes: 9 additions & 0 deletions src/tree/BranchDataItemWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type BranchDataItemOptions = {
defaults?: vscode.TreeItem;
portalUrl?: vscode.Uri;
viewProperties?: ViewPropertiesModel;
idPrefix?: string;
};

function appendContextValues(originalValues: string | undefined, optionsValues: string[] | undefined, extraValues: string[] | undefined): string {
Expand Down Expand Up @@ -49,6 +50,7 @@ export class BranchDataItemWrapper implements ResourceGroupsItem, Wrapper {
} else {
this.id = this.branchItem.id ?? this?.options?.defaultId ?? uuidv4();
}
this.id = this.options?.idPrefix ? `${this.options.idPrefix}/${this.id}` : this.id;
}

public readonly id: string;
Expand All @@ -69,12 +71,18 @@ export class BranchDataItemWrapper implements ResourceGroupsItem, Wrapper {
factory(child, this.branchDataProvider, {
portalUrl: (child as AzureResourceModel).portalUrl,
viewProperties: (child as AzureResourceModel).viewProperties,
// recursively prefix child items with the account and tenant id
// this ensures that items provided by branch data providers are prefixed
idPrefix: this.options?.idPrefix,
})
);
}

async getTreeItem(): Promise<vscode.TreeItem> {
const treeItem = await this.branchDataProvider.getTreeItem(this.branchItem);
// set the id of the tree item to the id of the branch item
// we do this because the branch item has already modified the item's id (see constructor)
treeItem.id = this.id;

const contextValue = appendContextValues(treeItem.contextValue, this.options?.contextValues, this.getExtraContextValues());

Expand Down Expand Up @@ -119,5 +127,6 @@ export function createBranchDataItemFactory(itemCache: BranchDataItemCache): Bra
itemCache.createOrGetItem(
branchItem,
() => new BranchDataItemWrapper(branchItem, branchDataProvider, itemCache, options),
`${options?.idPrefix ?? ''}${branchItem.id}`,
)
}
12 changes: 9 additions & 3 deletions src/tree/ResourceTreeDataProviderBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export abstract class ResourceTreeDataProviderBase extends vscode.Disposable imp
for (const child of children) {
if (child.id.toLowerCase() === id.toLowerCase()) {
return child;
} else if (removePrefix(child.id.toLowerCase()) === id.toLowerCase()) {
return child;
} else if (this.isAncestorOf(child, id)) {
element = child;
continue outerLoop;
Expand All @@ -123,10 +125,14 @@ export abstract class ResourceTreeDataProviderBase extends vscode.Disposable imp
}

protected isAncestorOf(element: ResourceGroupsItem, id: string): boolean {
// remove accounts/<accountId>/tenant/<tenantId> from the beginning of the id
const elementId = element.id.replace(/\/accounts\/[^/]+\/tenants\/[^/]+\//i, '/').toLowerCase() + '/';
return id.toLowerCase().startsWith(elementId);
// remove accounts / <accountId>/tenant/<tenantId> from the beginning of the id
const elementId = removePrefix(element.id) + '/';
return id.toLowerCase().startsWith(elementId.toLowerCase());
}

protected abstract onGetChildren(element?: ResourceGroupsItem | undefined): Promise<ResourceGroupsItem[] | null | undefined>;
}

function removePrefix(id: string): string {
return id.replace(/\/accounts\/[^/]+\/tenants\/[^/]+\//i, '/')
}
6 changes: 4 additions & 2 deletions src/tree/azure/AzureResourceItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { createPortalUrl } from '../../utils/v2/createPortalUrl';
import { BranchDataItemCache } from '../BranchDataItemCache';
import { BranchDataItemOptions, BranchDataItemWrapper } from '../BranchDataItemWrapper';
import { ResourceGroupsItem } from '../ResourceGroupsItem';
import { createAzureIdPrefix } from './idPrefix';

export class AzureResourceItem<T extends AzureResource> extends BranchDataItemWrapper {
constructor(
Expand All @@ -27,7 +28,7 @@ export class AzureResourceItem<T extends AzureResource> extends BranchDataItemWr
}

override readonly portalUrl: Uri;
readonly id = this.resource.id;
readonly id = `${createAzureIdPrefix(this.resource.subscription)}${this.resource.id}`;
readonly tagsModel = new ResourceTags(this.resource);

override async getParent(): Promise<ResourceGroupsItem | undefined> {
Expand Down Expand Up @@ -57,6 +58,7 @@ export function createResourceItemFactory<T extends AzureResource>(itemCache: Br
return (resource, branchItem, branchDataProvider, parent, options) =>
itemCache.createOrGetItem(
branchItem,
() => new AzureResourceItem(resource, branchItem, branchDataProvider, itemCache, parent, options)
() => new AzureResourceItem(resource, branchItem, branchDataProvider, itemCache, parent, options),
`${createAzureIdPrefix(resource.subscription)}${resource.id}`,
);
}
5 changes: 3 additions & 2 deletions src/tree/azure/SubscriptionItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { callWithTelemetryAndErrorHandling, createSubscriptionContext, IActionContext, ISubscriptionContext, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
import { callWithTelemetryAndErrorHandling, createSubscriptionContext, IActionContext, ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { AzureSubscription } from "../../../api/src/index";
import { AzureResourceProviderManager } from "../../api/ResourceProviderManagers";
Expand All @@ -13,6 +13,7 @@ import { createPortalUrl } from "../../utils/v2/createPortalUrl";
import { ResourceGroupsItem } from "../ResourceGroupsItem";
import { ResourceGroupsTreeContext } from "../ResourceGroupsTreeContext";
import { AzureResourceGroupingManager } from "./grouping/AzureResourceGroupingManager";
import { createAzureIdPrefix } from "./idPrefix";

export class SubscriptionItem implements ResourceGroupsItem {
constructor(
Expand All @@ -28,7 +29,7 @@ export class SubscriptionItem implements ResourceGroupsItem {
...subscription
};

this.id = `/accounts/${nonNullValueAndProp(subscription.account, 'id')}/tenants/${subscription.tenantId}/subscriptions/${subscription.subscriptionId}`;
this.id = `${createAzureIdPrefix(this.subscription)}/subscriptions/${subscription.subscriptionId}`;
this.description = description ? description : '';

this.portalUrl = createPortalUrl(this.subscription, `/subscriptions/${this.subscription.subscriptionId}`);
Expand Down
7 changes: 5 additions & 2 deletions src/tree/azure/grouping/GroupingItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ResourceGroupsItem } from '../../ResourceGroupsItem';
import { ResourceGroupsTreeContext } from '../../ResourceGroupsTreeContext';
import { BranchDataProviderFactory } from '../AzureResourceBranchDataProviderManager';
import { ResourceItemFactory } from '../AzureResourceItem';
import { createAzureIdPrefix } from '../idPrefix';
import { GroupingItemFactoryOptions } from './GroupingItemFactory';

export class GroupingItem implements ResourceGroupsItem {
Expand Down Expand Up @@ -56,7 +57,7 @@ export class GroupingItem implements ResourceGroupsItem {
} : undefined;

if (this.context?.subscription) {
this.id = `/subscriptions/${this.context?.subscriptionContext.subscriptionId}/account/${this.context?.subscription.account?.id}/groupings/${this.label}`;
this.id = `${createAzureIdPrefix(this.context?.subscription)}/subscriptions/${this.context?.subscriptionContext.subscriptionId}/groupings/${this.label}`;
} else {
// favorites groups don't always have a subscription
this.id = `/groupings/${this.label}`;
Expand Down Expand Up @@ -119,7 +120,9 @@ export class GroupingItem implements ResourceGroupsItem {
viewProperties: resourceItem.viewProperties ?? {
label: resource.name,
data: resource.raw
}
},
// prefix child items with the account and tenant id
idPrefix: createAzureIdPrefix(resource.subscription),
};

items.push(this.resourceItemFactory(resource, resourceItem, branchDataProvider, this, options));
Expand Down
5 changes: 5 additions & 0 deletions src/tree/azure/idPrefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AzureSubscription } from "api/src/resources/azure";

export function createAzureIdPrefix(subscription: AzureSubscription): string {
return `/accounts/${subscription.account?.id}/tenants/${subscription.tenantId}`;
}
2 changes: 1 addition & 1 deletion test/api/viewProperties.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ suite('AzureResourceModel.viewProperties tests', async () => {
const functionGroup = groups!.find(g => g.label?.toString().includes('Func'));
const children = await tdp.getChildren(functionGroup) as BranchDataItemWrapper[];

const functionApp1Item = children.find(child => child.id === mockResources.functionApp1.id);
const functionApp1Item = children.find(child => child.id.endsWith(mockResources.functionApp1.id));
assert.ok(functionApp1Item);

assert.ok(hasViewProperties(functionApp1Item));
Expand Down

0 comments on commit 7b2ffa7

Please sign in to comment.