mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 23:16:56 +00:00
Get collection usage size with ARM metrics call (#327)
- Removed `readCollectionQuotaInfo` call. The only data we need is the usage size which we can get via the ARM metrics call instead. - Added `getCollectionUsageSize` which fetches the `DataUsage` and `IndexUsage` metrics, converts them to KB, and returns the sum as the total usage size
This commit is contained in:
parent
17fd2185dc
commit
9cbf632577
87
src/Common/dataAccess/getCollectionDataUsageSize.ts
Normal file
87
src/Common/dataAccess/getCollectionDataUsageSize.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { armRequest } from "../../Utils/arm/request";
|
||||||
|
import { configContext } from "../../ConfigContext";
|
||||||
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
|
interface TimeSeriesData {
|
||||||
|
data: {
|
||||||
|
timeStamp: string;
|
||||||
|
total: number;
|
||||||
|
}[];
|
||||||
|
metadatavalues: {
|
||||||
|
name: {
|
||||||
|
localizedValue: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MetricsData {
|
||||||
|
displayDescription: string;
|
||||||
|
errorCode: string;
|
||||||
|
id: string;
|
||||||
|
name: {
|
||||||
|
value: string;
|
||||||
|
localizedValue: string;
|
||||||
|
};
|
||||||
|
timeseries: TimeSeriesData[];
|
||||||
|
type: string;
|
||||||
|
unit: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MetricsResponse {
|
||||||
|
cost: number;
|
||||||
|
interval: string;
|
||||||
|
namespace: string;
|
||||||
|
resourceregion: string;
|
||||||
|
timespan: string;
|
||||||
|
value: MetricsData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
|
||||||
|
const subscriptionId = userContext.subscriptionId;
|
||||||
|
const resourceGroup = userContext.resourceGroup;
|
||||||
|
const accountName = userContext.databaseAccount.name;
|
||||||
|
const filter = `DatabaseName eq '${databaseName}' and CollectionName eq '${containerName}'`;
|
||||||
|
const metricNames = "DataUsage,IndexUsage";
|
||||||
|
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/providers/microsoft.insights/metrics`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const metricsResponse: MetricsResponse = await armRequest({
|
||||||
|
host: configContext.ARM_ENDPOINT,
|
||||||
|
path,
|
||||||
|
method: "GET",
|
||||||
|
apiVersion: "2018-01-01",
|
||||||
|
queryParams: {
|
||||||
|
filter,
|
||||||
|
metricNames
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (metricsResponse?.value?.length !== 2) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataUsageData: MetricsData = metricsResponse.value[0];
|
||||||
|
const indexUsagedata: MetricsData = metricsResponse.value[1];
|
||||||
|
const dataUsageSizeInKb: number = getUsageSizeInKb(dataUsageData);
|
||||||
|
const indexUsageSizeInKb: number = getUsageSizeInKb(indexUsagedata);
|
||||||
|
|
||||||
|
return dataUsageSizeInKb + indexUsageSizeInKb;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "getCollectionUsageSize");
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUsageSizeInKb = (metricsData: MetricsData): number => {
|
||||||
|
if (metricsData?.errorCode !== "Success") {
|
||||||
|
throw Error(`Get collection usage size failed: ${metricsData.errorCode}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeSeriesData: TimeSeriesData = metricsData?.timeseries?.[0];
|
||||||
|
const usageSizeInBytes: number = timeSeriesData?.data?.[0]?.total;
|
||||||
|
|
||||||
|
return usageSizeInBytes ? usageSizeInBytes / 1024 : 0;
|
||||||
|
};
|
@ -14,7 +14,7 @@ export async function getIndexTransformationProgress(databaseId: string, collect
|
|||||||
const response = await client()
|
const response = await client()
|
||||||
.database(databaseId)
|
.database(databaseId)
|
||||||
.container(collectionId)
|
.container(collectionId)
|
||||||
.read({ populateQuotaInfo: true });
|
.read();
|
||||||
|
|
||||||
indexTransformationPercentage = parseInt(
|
indexTransformationPercentage = parseInt(
|
||||||
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string
|
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import * as HeadersUtility from "../HeadersUtility";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { ContainerDefinition, Resource } from "@azure/cosmos";
|
|
||||||
import { HttpHeaders } from "../Constants";
|
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
|
||||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
|
|
||||||
interface ResourceWithStatistics {
|
|
||||||
statistics: DataModels.Statistic[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const readCollectionQuotaInfo = async (
|
|
||||||
collection: ViewModels.Collection
|
|
||||||
): Promise<DataModels.CollectionQuotaInfo> => {
|
|
||||||
const clearMessage = logConsoleProgress(`Querying containers for database ${collection.id}`);
|
|
||||||
const options: RequestOptions = {};
|
|
||||||
options.populateQuotaInfo = true;
|
|
||||||
options.initialHeaders = options.initialHeaders || {};
|
|
||||||
options.initialHeaders[HttpHeaders.populatePartitionStatistics] = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.read(options);
|
|
||||||
const quota: DataModels.CollectionQuotaInfo = HeadersUtility.getQuota(response.headers);
|
|
||||||
const resource = response.resource as ContainerDefinition & Resource & ResourceWithStatistics;
|
|
||||||
quota["usageSizeInKB"] = resource.statistics.reduce(
|
|
||||||
(previousValue: number, currentValue: DataModels.Statistic) => previousValue + currentValue.sizeInKB,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
quota["numPartitions"] = resource.statistics.length;
|
|
||||||
quota["uniqueKeyPolicy"] = collection.uniqueKeyPolicy; // TODO: Remove after refactoring (#119617)
|
|
||||||
|
|
||||||
return quota;
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, "ReadCollectionQuotaInfo", `Error while querying quota info for container ${collection.id}`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
clearMessage();
|
|
||||||
}
|
|
||||||
};
|
|
@ -230,18 +230,6 @@ export interface SDKOfferDefinition extends Resource {
|
|||||||
offerResourceId?: string;
|
offerResourceId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionQuotaInfo {
|
|
||||||
storedProcedures: number;
|
|
||||||
triggers: number;
|
|
||||||
functions: number;
|
|
||||||
documentsSize: number;
|
|
||||||
collectionSize: number;
|
|
||||||
documentsCount: number;
|
|
||||||
usageSizeInKB: number;
|
|
||||||
numPartitions: number;
|
|
||||||
uniqueKeyPolicy?: UniqueKeyPolicy; // TODO: This should ideally not be a part of the collection quota. Remove after refactoring. (#119617)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OfferThroughputInfo {
|
export interface OfferThroughputInfo {
|
||||||
minimumRUForCollection: number;
|
minimumRUForCollection: number;
|
||||||
numPhysicalPartitions: number;
|
numPhysicalPartitions: number;
|
||||||
|
@ -120,7 +120,7 @@ export interface Collection extends CollectionBase {
|
|||||||
requestSchema?: () => void;
|
requestSchema?: () => void;
|
||||||
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
||||||
uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
||||||
quotaInfo: ko.Observable<DataModels.CollectionQuotaInfo>;
|
usageSizeInKB: ko.Observable<number>;
|
||||||
offer: ko.Observable<DataModels.Offer>;
|
offer: ko.Observable<DataModels.Offer>;
|
||||||
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
||||||
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||||
|
@ -186,7 +186,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
onScaleSaveableChange={this.props.onScaleSaveableChange}
|
onScaleSaveableChange={this.props.onScaleSaveableChange}
|
||||||
onScaleDiscardableChange={this.props.onScaleDiscardableChange}
|
onScaleDiscardableChange={this.props.onScaleDiscardableChange}
|
||||||
getThroughputWarningMessage={this.getThroughputWarningMessage}
|
getThroughputWarningMessage={this.getThroughputWarningMessage}
|
||||||
usageSizeInKB={this.props.collection.quotaInfo().usageSizeInKB}
|
usageSizeInKB={this.props.collection.usageSizeInKB()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
|
|||||||
spendAckChecked={false}
|
spendAckChecked={false}
|
||||||
throughput={1000}
|
throughput={1000}
|
||||||
throughputBaseline={1000}
|
throughputBaseline={1000}
|
||||||
|
usageSizeInKB={100}
|
||||||
wasAutopilotOriginallySet={true}
|
wasAutopilotOriginallySet={true}
|
||||||
/>
|
/>
|
||||||
<Stack
|
<Stack
|
||||||
|
@ -18,7 +18,7 @@ export const collection = ({
|
|||||||
excludedPaths: []
|
excludedPaths: []
|
||||||
}),
|
}),
|
||||||
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
||||||
quotaInfo: ko.observable<DataModels.CollectionQuotaInfo>({} as DataModels.CollectionQuotaInfo),
|
usageSizeInKB: ko.observable(100),
|
||||||
offer: ko.observable<DataModels.Offer>({
|
offer: ko.observable<DataModels.Offer>({
|
||||||
autoscaleMaxThroughput: undefined,
|
autoscaleMaxThroughput: undefined,
|
||||||
manualThroughput: 10000,
|
manualThroughput: 10000,
|
||||||
|
@ -1300,9 +1300,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperty": "partitionKey",
|
"partitionKeyProperty": "partitionKey",
|
||||||
"quotaInfo": [Function],
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": Object {},
|
||||||
|
"usageSizeInKB": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container={
|
container={
|
||||||
@ -3871,9 +3871,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperty": "partitionKey",
|
"partitionKeyProperty": "partitionKey",
|
||||||
"quotaInfo": [Function],
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": Object {},
|
||||||
|
"usageSizeInKB": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container={
|
container={
|
||||||
|
@ -10,10 +10,9 @@ describe("Collection", () => {
|
|||||||
container: Explorer,
|
container: Explorer,
|
||||||
databaseId: string,
|
databaseId: string,
|
||||||
data: DataModels.Collection,
|
data: DataModels.Collection,
|
||||||
quotaInfo: DataModels.CollectionQuotaInfo,
|
|
||||||
offer: DataModels.Offer
|
offer: DataModels.Offer
|
||||||
): Collection {
|
): Collection {
|
||||||
return new Collection(container, databaseId, data, quotaInfo, offer);
|
return new Collection(container, databaseId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateMockCollectionsDataModelWithPartitionKey(
|
function generateMockCollectionsDataModelWithPartitionKey(
|
||||||
@ -50,7 +49,7 @@ describe("Collection", () => {
|
|||||||
});
|
});
|
||||||
mockContainer.deleteCollectionText = ko.observable<string>("delete collection");
|
mockContainer.deleteCollectionText = ko.observable<string>("delete collection");
|
||||||
|
|
||||||
return generateCollection(mockContainer, "abc", data, {} as DataModels.CollectionQuotaInfo, {} as DataModels.Offer);
|
return generateCollection(mockContainer, "abc", data, {} as DataModels.Offer);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("Partition key path parsing", () => {
|
describe("Partition key path parsing", () => {
|
||||||
|
@ -10,7 +10,7 @@ import { readTriggers } from "../../Common/dataAccess/readTriggers";
|
|||||||
import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions";
|
import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions";
|
||||||
import { createDocument } from "../../Common/DocumentClientUtilityBase";
|
import { createDocument } from "../../Common/DocumentClientUtilityBase";
|
||||||
import { readCollectionOffer } from "../../Common/dataAccess/readCollectionOffer";
|
import { readCollectionOffer } from "../../Common/dataAccess/readCollectionOffer";
|
||||||
import { readCollectionQuotaInfo } from "../../Common/dataAccess/readCollectionQuotaInfo";
|
import { getCollectionUsageSizeInKB } from "../../Common/dataAccess/getCollectionDataUsageSize";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@ -37,7 +37,6 @@ import UserDefinedFunction from "./UserDefinedFunction";
|
|||||||
import { configContext, Platform } from "../../ConfigContext";
|
import { configContext, Platform } from "../../ConfigContext";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
|
||||||
import { fetchPortalNotifications } from "../../Common/PortalNotifications";
|
import { fetchPortalNotifications } from "../../Common/PortalNotifications";
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
@ -54,7 +53,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
public defaultTtl: ko.Observable<number>;
|
public defaultTtl: ko.Observable<number>;
|
||||||
public indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
public indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
||||||
public uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
public uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
||||||
public quotaInfo: ko.Observable<DataModels.CollectionQuotaInfo>;
|
public usageSizeInKB: ko.Observable<number>;
|
||||||
|
|
||||||
public offer: ko.Observable<DataModels.Offer>;
|
public offer: ko.Observable<DataModels.Offer>;
|
||||||
public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
||||||
public changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
public changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||||
@ -95,13 +95,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
public userDefinedFunctionsFocused: ko.Observable<boolean>;
|
public userDefinedFunctionsFocused: ko.Observable<boolean>;
|
||||||
public triggersFocused: ko.Observable<boolean>;
|
public triggersFocused: ko.Observable<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(container: Explorer, databaseId: string, data: DataModels.Collection) {
|
||||||
container: Explorer,
|
|
||||||
databaseId: string,
|
|
||||||
data: DataModels.Collection,
|
|
||||||
quotaInfo: DataModels.CollectionQuotaInfo,
|
|
||||||
offer: DataModels.Offer
|
|
||||||
) {
|
|
||||||
this.nodeKind = "Collection";
|
this.nodeKind = "Collection";
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.self = data._self;
|
this.self = data._self;
|
||||||
@ -113,8 +107,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
this.id = ko.observable(data.id);
|
this.id = ko.observable(data.id);
|
||||||
this.defaultTtl = ko.observable(data.defaultTtl);
|
this.defaultTtl = ko.observable(data.defaultTtl);
|
||||||
this.indexingPolicy = ko.observable(data.indexingPolicy);
|
this.indexingPolicy = ko.observable(data.indexingPolicy);
|
||||||
this.quotaInfo = ko.observable(quotaInfo);
|
this.usageSizeInKB = ko.observable();
|
||||||
this.offer = ko.observable(offer);
|
this.offer = ko.observable();
|
||||||
this.conflictResolutionPolicy = ko.observable(data.conflictResolutionPolicy);
|
this.conflictResolutionPolicy = ko.observable(data.conflictResolutionPolicy);
|
||||||
this.changeFeedPolicy = ko.observable<DataModels.ChangeFeedPolicy>(data.changeFeedPolicy);
|
this.changeFeedPolicy = ko.observable<DataModels.ChangeFeedPolicy>(data.changeFeedPolicy);
|
||||||
this.analyticalStorageTtl = ko.observable(data.analyticalStorageTtl);
|
this.analyticalStorageTtl = ko.observable(data.analyticalStorageTtl);
|
||||||
@ -607,14 +601,6 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private async loadCollectionQuotaInfo(): Promise<void> {
|
|
||||||
// TODO: Use the collection entity cache to get quota info
|
|
||||||
const quotaInfoWithUniqueKeyPolicy = await readCollectionQuotaInfo(this);
|
|
||||||
this.uniqueKeyPolicy = quotaInfoWithUniqueKeyPolicy.uniqueKeyPolicy;
|
|
||||||
const quotaInfo = _.omit(quotaInfoWithUniqueKeyPolicy, "uniqueKeyPolicy");
|
|
||||||
this.quotaInfo(quotaInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||||
const collection: ViewModels.Collection = source.collection || source;
|
const collection: ViewModels.Collection = source.collection || source;
|
||||||
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||||
@ -1287,7 +1273,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.offer(await readCollectionOffer(params));
|
this.offer(await readCollectionOffer(params));
|
||||||
await this.loadCollectionQuotaInfo();
|
this.usageSizeInKB(await getCollectionUsageSizeInKB(this.databaseId, this.id()));
|
||||||
|
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.LoadOffers,
|
Action.LoadOffers,
|
||||||
|
@ -193,7 +193,7 @@ export default class Database implements ViewModels.Database {
|
|||||||
});
|
});
|
||||||
|
|
||||||
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
|
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
|
||||||
const collectionVM: Collection = new Collection(this.container, this.id(), collection, null, null);
|
const collectionVM: Collection = new Collection(this.container, this.id(), collection);
|
||||||
collectionVMs.push(collectionVM);
|
collectionVMs.push(collectionVM);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -229,9 +229,7 @@ const createMockCollection = (): ViewModels.Collection => {
|
|||||||
const mockCollectionVM: ViewModels.Collection = new Collection(
|
const mockCollectionVM: ViewModels.Collection = new Collection(
|
||||||
createMockContainer(),
|
createMockContainer(),
|
||||||
"fakeDatabaseId",
|
"fakeDatabaseId",
|
||||||
mockCollection,
|
mockCollection
|
||||||
undefined,
|
|
||||||
undefined
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return mockCollectionVM;
|
return mockCollectionVM;
|
||||||
|
@ -33,18 +33,36 @@ export class ARMError extends Error {
|
|||||||
public code: string | number;
|
public code: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ARMQueryParams {
|
||||||
|
filter?: string;
|
||||||
|
metricNames?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
host: string;
|
host: string;
|
||||||
path: string;
|
path: string;
|
||||||
apiVersion: string;
|
apiVersion: string;
|
||||||
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD";
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD";
|
||||||
body?: unknown;
|
body?: unknown;
|
||||||
|
queryParams?: ARMQueryParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is very similar to what is happening in ResourceProviderClient.ts. Should probably merge them.
|
// TODO: This is very similar to what is happening in ResourceProviderClient.ts. Should probably merge them.
|
||||||
export async function armRequest<T>({ host, path, apiVersion, method, body: requestBody }: Options): Promise<T> {
|
export async function armRequest<T>({
|
||||||
|
host,
|
||||||
|
path,
|
||||||
|
apiVersion,
|
||||||
|
method,
|
||||||
|
body: requestBody,
|
||||||
|
queryParams
|
||||||
|
}: Options): Promise<T> {
|
||||||
const url = new URL(path, host);
|
const url = new URL(path, host);
|
||||||
url.searchParams.append("api-version", configContext.armAPIVersion || apiVersion);
|
url.searchParams.append("api-version", configContext.armAPIVersion || apiVersion);
|
||||||
|
if (queryParams) {
|
||||||
|
queryParams.filter && url.searchParams.append("$filter", queryParams.filter);
|
||||||
|
queryParams.metricNames && url.searchParams.append("metricnames", queryParams.metricNames);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await window.fetch(url.href, {
|
const response = await window.fetch(url.href, {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
|
Loading…
Reference in New Issue
Block a user