mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-16 17:25:58 +00:00
Poll on Location header for operation status (#309)
This commit is contained in:
parent
e6ca1d25c9
commit
3e782527d0
@ -21,13 +21,12 @@ describe("ARM request", () => {
|
|||||||
|
|
||||||
it("should poll for async operations", async () => {
|
it("should poll for async operations", async () => {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.set("azure-asyncoperation", "https://foo.com/operationStatus");
|
headers.set("location", "https://foo.com/operationStatus");
|
||||||
window.fetch = jest.fn().mockResolvedValue({
|
window.fetch = jest.fn().mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
headers,
|
headers,
|
||||||
json: async () => {
|
status: 200,
|
||||||
return { status: "Succeeded" };
|
json: async () => ({})
|
||||||
}
|
|
||||||
});
|
});
|
||||||
await armRequest({ apiVersion: "2001-01-01", host: "https://foo.com", path: "foo", method: "GET" });
|
await armRequest({ apiVersion: "2001-01-01", host: "https://foo.com", path: "foo", method: "GET" });
|
||||||
expect(window.fetch).toHaveBeenCalledTimes(2);
|
expect(window.fetch).toHaveBeenCalledTimes(2);
|
||||||
@ -35,10 +34,11 @@ describe("ARM request", () => {
|
|||||||
|
|
||||||
it("should throw for failed async operations", async () => {
|
it("should throw for failed async operations", async () => {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.set("azure-asyncoperation", "https://foo.com/operationStatus");
|
headers.set("location", "https://foo.com/operationStatus");
|
||||||
window.fetch = jest.fn().mockResolvedValue({
|
window.fetch = jest.fn().mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
headers,
|
headers,
|
||||||
|
status: 200,
|
||||||
json: async () => {
|
json: async () => {
|
||||||
return { status: "Failed" };
|
return { status: "Failed" };
|
||||||
}
|
}
|
||||||
|
@ -70,55 +70,35 @@ export async function armRequest<T>({ host, path, apiVersion, method, body: requ
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const operationStatusUrl = response.headers && response.headers.get("azure-asyncoperation");
|
const operationStatusUrl = response.headers && response.headers.get("location");
|
||||||
if (operationStatusUrl) {
|
if (operationStatusUrl) {
|
||||||
await promiseRetry(() => getOperationStatus(operationStatusUrl));
|
return await promiseRetry(() => getOperationStatus(operationStatusUrl));
|
||||||
// TODO: ARM is supposed to return a resourceLocation property, but it does not https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#target-resource-location
|
|
||||||
// When Cosmos RP adds resourceLocation, we should use it instead
|
|
||||||
// For now manually execute a GET if the operation was a mutation and not a deletion
|
|
||||||
if (method === "POST" || method === "PATCH" || method === "PUT") {
|
|
||||||
return armRequest({
|
|
||||||
host,
|
|
||||||
path,
|
|
||||||
apiVersion,
|
|
||||||
method: "GET"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseBody = (await response.json()) as T;
|
const responseBody = (await response.json()) as T;
|
||||||
return responseBody;
|
return responseBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SUCCEEDED = "Succeeded" as const;
|
|
||||||
const FAILED = "Failed" as const;
|
|
||||||
const CANCELED = "Canceled" as const;
|
|
||||||
|
|
||||||
type Status = typeof SUCCEEDED | typeof FAILED | typeof CANCELED;
|
|
||||||
|
|
||||||
interface OperationResponse {
|
|
||||||
status: Status;
|
|
||||||
error: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getOperationStatus(operationStatusUrl: string) {
|
async function getOperationStatus(operationStatusUrl: string) {
|
||||||
const response = await window.fetch(operationStatusUrl, {
|
const response = await window.fetch(operationStatusUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: userContext.authorizationToken
|
Authorization: userContext.authorizationToken
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorResponse = (await response.json()) as ErrorResponse;
|
const errorResponse = (await response.json()) as ErrorResponse;
|
||||||
const error = new Error(errorResponse.message) as ARMError;
|
const error = new Error(errorResponse.message) as ARMError;
|
||||||
error.code = errorResponse.code;
|
error.code = errorResponse.code;
|
||||||
throw new AbortError(error);
|
throw new AbortError(error);
|
||||||
}
|
}
|
||||||
const body = (await response.json()) as OperationResponse;
|
|
||||||
|
const body = await response.json();
|
||||||
const status = body.status;
|
const status = body.status;
|
||||||
if (status === SUCCEEDED) {
|
if (!status && response.status === 200) {
|
||||||
return;
|
return body;
|
||||||
}
|
}
|
||||||
if (status === CANCELED || status === FAILED) {
|
if (status === "Canceled" || status === "Failed") {
|
||||||
const errorMessage = body.error ? JSON.stringify(body.error) : "Operation could not be completed";
|
const errorMessage = body.error ? JSON.stringify(body.error) : "Operation could not be completed";
|
||||||
const error = new Error(errorMessage);
|
const error = new Error(errorMessage);
|
||||||
throw new AbortError(error);
|
throw new AbortError(error);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user