From 49674869e07785d625b9bc355d69c8f5bb87957c Mon Sep 17 00:00:00 2001 From: Melissa Neubert Date: Thu, 28 Oct 2021 11:58:11 -0700 Subject: [PATCH 1/6] Initial delete & tests --- .../communication-callingserver/package.json | 2 +- .../review/communication-callingserver.api.md | 1 + .../src/ContentDownloader.ts | 14 +-- .../src/callingServerClient.ts | 90 ++++++++++++++++++- .../src/utils/utils.ts | 18 ++++ .../test/public/deleteContent.spec.ts | 89 ++++++++++++++++++ 6 files changed, 197 insertions(+), 17 deletions(-) create mode 100644 sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts diff --git a/sdk/communication/communication-callingserver/package.json b/sdk/communication/communication-callingserver/package.json index 5f513a058852..ebfc403c968e 100644 --- a/sdk/communication/communication-callingserver/package.json +++ b/sdk/communication/communication-callingserver/package.json @@ -71,7 +71,7 @@ "@azure/communication-common": "^1.1.0", "@azure/core-auth": "^1.3.0", "@azure/core-http": "^2.0.0", - "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-tracing": "1.0.0-preview.14", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0", diff --git a/sdk/communication/communication-callingserver/review/communication-callingserver.api.md b/sdk/communication/communication-callingserver/review/communication-callingserver.api.md index d6c1f190931f..192749714745 100644 --- a/sdk/communication/communication-callingserver/review/communication-callingserver.api.md +++ b/sdk/communication/communication-callingserver/review/communication-callingserver.api.md @@ -85,6 +85,7 @@ export class CallingServerClient { cancelMediaOperation(callLocator: CallLocator, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; cancelParticipantMediaOperation(callLocator: CallLocator, participant: CommunicationIdentifier, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; createCallConnection(source: CommunicationIdentifier, targets: CommunicationIdentifier[], options: CreateCallConnectionOptions): Promise; + delete(deleteUri: string, options?: OperationOptions): Promise; download(uri: string, offset?: number, options?: DownloadOptions): Promise; getCallConnection(callConnectionId: string): CallConnection; // Warning: (ae-forgotten-export) The symbol "CallRecordingProperties" needs to be exported by the entry point index.d.ts diff --git a/sdk/communication/communication-callingserver/src/ContentDownloader.ts b/sdk/communication/communication-callingserver/src/ContentDownloader.ts index 23527da31125..e2df1edfab5c 100644 --- a/sdk/communication/communication-callingserver/src/ContentDownloader.ts +++ b/sdk/communication/communication-callingserver/src/ContentDownloader.ts @@ -8,7 +8,6 @@ import * as Mappers from "./generated/src/models/mappers"; import * as ExtraMappers from "./mappers"; import { CallingServerApiClientContext } from "./generated/src/callingServerApiClientContext"; import { URLBuilder } from "@azure/core-http"; -import { OperationQueryParameter } from "@azure/core-http"; import { createSpan } from "./tracing"; import { SpanStatusCode } from "@azure/core-tracing"; @@ -20,6 +19,7 @@ import { OperationSpec } from "@azure/core-http"; import { ContentDownloadResponse } from "."; +import { CallingServerUtils } from "./utils/utils"; export class ContentDownloader { private readonly client: CallingServerApiClientContext; @@ -79,17 +79,7 @@ export class ContentDownloader { const serializer = new Serializer(Mappers, /* isXml */ false); function getDownloadContentOperationSpec(url: string, stringToSign: string): OperationSpec { - const stringToSignHeader: OperationQueryParameter = { - parameterPath: "UriToSignWith", - mapper: { - defaultValue: stringToSign, - isConstant: true, - serializedName: "UriToSignWith", - type: { - name: "String" - } - } - }; + const stringToSignHeader = CallingServerUtils.getStringToSignHeader(stringToSign); const downloadContentOperationSpec: OperationSpec = { path: "", diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index fd1dc24e4466..c5dc51dadbcc 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -35,6 +35,7 @@ import { StartCallRecordingWithCallLocatorRequest, CallRecordingProperties } from "./generated/src/models"; +import * as Mappers from "./generated/src/models/mappers"; import { TokenCredential } from "@azure/core-auth"; import { @@ -50,7 +51,12 @@ import { InternalPipelineOptions, createPipelineFromOptions, operationOptionsToRequestOptionsBase, - RestResponse + RestResponse, + OperationArguments, + OperationOptions, + OperationSpec, + Serializer, + URLBuilder } from "@azure/core-http"; import { SpanStatusCode } from "@azure/core-tracing"; import { CallingServerApiClient } from "./generated/src/callingServerApiClient"; @@ -85,7 +91,7 @@ export class CallingServerClient { private readonly callingServerServiceClient: CallingServerApiClient; private readonly callConnectionRestClient: CallConnections; private readonly serverCallRestClient: ServerCalls; - private readonly downloadCallingServerApiClient: CallingServerApiClientContext; + private readonly deleteAndDownloadCallingServerApiClient: CallingServerApiClientContext; /** * Initializes a new instance of the CallingServerClient class. @@ -138,7 +144,7 @@ export class CallingServerClient { this.callingServerServiceClient = new CallingServerApiClient(url, pipeline); this.callConnectionRestClient = this.callingServerServiceClient.callConnections; this.serverCallRestClient = this.callingServerServiceClient.serverCalls; - this.downloadCallingServerApiClient = new CallingServerApiClientContext(url, pipeline); + this.deleteAndDownloadCallingServerApiClient = new CallingServerApiClientContext(url, pipeline); } /** @@ -150,7 +156,7 @@ export class CallingServerClient { } public initializeContentDownloader(): ContentDownloader { - return new ContentDownloader(this.downloadCallingServerApiClient); + return new ContentDownloader(this.deleteAndDownloadCallingServerApiClient); } /** @@ -824,4 +830,80 @@ export class CallingServerClient { span.end(); } } + + /** + * Deletes the content pointed to the uri passed as a parameter. + * + * * In Node.js, data returns ? + * * In browsers, data returns ? + * + * @param deleteUri - Endpoint where the content exists. + * + * Example usage (Node.js): + * + * ```js + * // Delete and convert a blob to a string + * const deleteUri = "https://deleteUri.com"; + * const deleteResponse = await callingServerClient.delete(deleteUri); + * + * ``` + */ + public async delete( + deleteUri: string, + options: OperationOptions = {} + ): Promise + { + const { span, updatedOptions } = createSpan("ServerCallRestClient-delete", options); + + const operationArguments: OperationArguments = { + options: operationOptionsToRequestOptionsBase(updatedOptions) + }; + + try + { + const q = URLBuilder.parse(deleteUri); + const formattedUrl = q.getPath()!.startsWith("/") ? q.getPath()!.substr(1) : q.getPath()!; + const stringToSign = this.deleteAndDownloadCallingServerApiClient.endpoint + formattedUrl; + + return this.deleteAndDownloadCallingServerApiClient.sendOperationRequest( + operationArguments, + getDeleteOperationSpec(deleteUri, stringToSign) + ) as Promise + } catch(e) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: e.message + }); + throw e; + } finally { + span.end(); + } + + } +} + + +function getDeleteOperationSpec(url: string, stringToSign: string): OperationSpec { + // Operation Specifications + const serializer = new Serializer(Mappers, /* isXml */ false); + const stringToSignHeader = CallingServerUtils.getStringToSignHeader(stringToSign); + + const deleteOperationSpec: OperationSpec = { + path: "", + baseUrl: url, + httpMethod: "DELETE", + responses: { + 200: {}, + default: { + bodyMapper: Mappers.CommunicationErrorResponse + } + }, + requestBody: undefined, + queryParameters: [], + urlParameters: [], + headerParameters: [stringToSignHeader], + serializer + }; + + return deleteOperationSpec; } diff --git a/sdk/communication/communication-callingserver/src/utils/utils.ts b/sdk/communication/communication-callingserver/src/utils/utils.ts index 540c53100081..0aa59ebc2fd3 100644 --- a/sdk/communication/communication-callingserver/src/utils/utils.ts +++ b/sdk/communication/communication-callingserver/src/utils/utils.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { OperationQueryParameter } from "@azure/core-http"; import { URLBuilder } from "@azure/core-http"; export class CallingServerUtils { @@ -13,4 +14,21 @@ export class CallingServerUtils { } return url.getScheme() === "http" || url.getScheme() === "https"; } + + public static getStringToSignHeader( + stringToSign: string + ): OperationQueryParameter + { + return { + parameterPath: "UriToSignWith", + mapper: { + defaultValue: stringToSign, + isConstant: true, + serializedName: "UriToSignWith", + type: { + name: "String" + } + } + }; + } } diff --git a/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts b/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts new file mode 100644 index 000000000000..7573df1ffd44 --- /dev/null +++ b/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + env, + isPlaybackMode, + record, + Recorder, + RecorderEnvironmentSetup + } from "@azure-tools/test-recorder"; + import { assert } from "chai"; + import { CallingServerClient } from "../../src"; + import { Context } from "mocha"; + import { RestError } from "@azure/core-http"; + + const replaceableVariables: { [k: string]: string } = { + COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING: "endpoint=https://endpoint/;accesskey=banana" + }; + + const environmentSetup: RecorderEnvironmentSetup = { + replaceableVariables, + customizationsOnRecordings: [ + (recording: string): string => recording.replace(/(https:\/\/)([^/',]*)/, "$1endpoint"), + (recording: string): string => recording.replace("endpoint:443", "endpoint") + ], + queryParametersToSkip: [] + }; + + describe("Delete Live Tests", function() { + let recorder: Recorder; + + const uri = + "https://endpoint/v1/objects/0-eus-d8-24d744599871098c7a22f28a4cb738d5"; + + const callingServerServiceClient = new CallingServerClient( + env.COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING || + "endpoint=https://endpoint/;accesskey=banana" + ); + + beforeEach(async function(this: Context) { + recorder = record(this, environmentSetup); + }); + + afterEach(async function(this: Context) { + if (!this.currentTest?.isPending()) { + await recorder.stop(); + } + }); + + it("delete", async function(this: Context) { + if (!isPlaybackMode()) { + // tslint:disable-next-line:no-invalid-this + this.skip(); + } + + const deleteRepsonse = await callingServerServiceClient.delete(uri); + assert.strictEqual(200, deleteRepsonse._response.status); + }); + + it("unauthorized delete", async function(this: Context) { + if (!isPlaybackMode()) { + // tslint:disable-next-line:no-invalid-this + this.skip(); + } + + try { + const unauthorizedCallingServerServiceClient = new CallingServerClient( + "endpoint=https://test.communication.azure.com/;accesskey=1234" + ) + await unauthorizedCallingServerServiceClient.delete(uri); + } catch (e) { + assert.equal((e as RestError).statusCode, 401); + } + }); + + it("invalid file delete", async function(this: Context) { + if (!isPlaybackMode()) { + // tslint:disable-next-line:no-invalid-this + this.skip(); + } + const invalidUri = "https://storage.asm.skype.com/v1/objects/0-eus-d3-00000000000000000000000000000000"; + try { + await callingServerServiceClient.download(invalidUri); + } catch (e) { + assert.equal((e as RestError).statusCode, 404); + } + }); +}); + From 4cd0e9076c5a6d08f7f539acc5241d84cd04f655 Mon Sep 17 00:00:00 2001 From: Melissa Neubert Date: Tue, 2 Nov 2021 11:07:27 -0700 Subject: [PATCH 2/6] Tests for delete apis --- .../communication-callingserver/package.json | 2 +- .../delete_live_tests/recording_delete.json | 23 +++++++++++++++++++ .../recording_invalid_file_delete.json | 23 +++++++++++++++++++ .../recording_unauthorized_delete.json | 23 +++++++++++++++++++ .../delete_live_tests/recording_delete.js | 18 +++++++++++++++ .../recording_invalid_file_delete.js | 18 +++++++++++++++ .../recording_unauthorized_delete.js | 18 +++++++++++++++ .../src/callingServerClient.ts | 3 ++- .../src/utils/utils.ts | 19 +++++++++++++++ .../test/public/deleteContent.spec.ts | 14 +++++------ 10 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_delete.json create mode 100644 sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_invalid_file_delete.json create mode 100644 sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_unauthorized_delete.json create mode 100644 sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_delete.js create mode 100644 sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_invalid_file_delete.js create mode 100644 sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_unauthorized_delete.js diff --git a/sdk/communication/communication-callingserver/package.json b/sdk/communication/communication-callingserver/package.json index ebfc403c968e..5f513a058852 100644 --- a/sdk/communication/communication-callingserver/package.json +++ b/sdk/communication/communication-callingserver/package.json @@ -71,7 +71,7 @@ "@azure/communication-common": "^1.1.0", "@azure/core-auth": "^1.3.0", "@azure/core-http": "^2.0.0", - "@azure/core-tracing": "1.0.0-preview.14", + "@azure/core-tracing": "1.0.0-preview.13", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0", diff --git a/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_delete.json b/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_delete.json new file mode 100644 index 000000000000..32931dae0f85 --- /dev/null +++ b/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_delete.json @@ -0,0 +1,23 @@ +{ + "recordings": [ + { + "method": "DELETE", + "url": "https://endpoint/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05", + "query": {}, + "requestBody": null, + "status": 200, + "response": "", + "responseHeaders": { + "content-length": "0", + "date": "Tue, 02 Nov 2021 17:49:08 GMT", + "server": "Microsoft-HTTPAPI/2.0", + "strict-transport-security": "max-age=31536000; includeSubDomains" + } + } + ], + "uniqueTestInfo": { + "uniqueName": {}, + "newDate": {} + }, + "hash": "e810745688333ce018b48166e9e854e3" +} \ No newline at end of file diff --git a/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_invalid_file_delete.json b/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_invalid_file_delete.json new file mode 100644 index 000000000000..97de0a6c7512 --- /dev/null +++ b/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_invalid_file_delete.json @@ -0,0 +1,23 @@ +{ + "recordings": [ + { + "method": "GET", + "url": "https://endpoint/v1/objects/0-wus-d4-ca4017a32f8514aa9f054f0917270000", + "query": {}, + "requestBody": null, + "status": 404, + "response": "", + "responseHeaders": { + "content-length": "0", + "date": "Tue, 02 Nov 2021 17:47:04 GMT", + "server": "Microsoft-HTTPAPI/2.0", + "strict-transport-security": "max-age=31536000; includeSubDomains" + } + } + ], + "uniqueTestInfo": { + "uniqueName": {}, + "newDate": {} + }, + "hash": "a29d365489ae98459d067c46c720becc" +} \ No newline at end of file diff --git a/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_unauthorized_delete.json b/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_unauthorized_delete.json new file mode 100644 index 000000000000..0a8d608ae5ee --- /dev/null +++ b/sdk/communication/communication-callingserver/recordings/browsers/delete_live_tests/recording_unauthorized_delete.json @@ -0,0 +1,23 @@ +{ + "recordings": [ + { + "method": "DELETE", + "url": "https://endpoint/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05", + "query": {}, + "requestBody": null, + "status": 401, + "response": "", + "responseHeaders": { + "content-length": "0", + "date": "Tue, 02 Nov 2021 17:47:04 GMT", + "server": "Microsoft-HTTPAPI/2.0", + "strict-transport-security": "max-age=31536000; includeSubDomains" + } + } + ], + "uniqueTestInfo": { + "uniqueName": {}, + "newDate": {} + }, + "hash": "1b018902befb4c128b53cbe8d7af4a82" +} \ No newline at end of file diff --git a/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_delete.js b/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_delete.js new file mode 100644 index 000000000000..ebd767717c33 --- /dev/null +++ b/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_delete.js @@ -0,0 +1,18 @@ +let nock = require('nock'); + +module.exports.hash = "2edca15d57c1511a41d4cbd310f4422b"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://endpoint', {"encodedQueryParams":true}) + .delete('/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05') + .reply(200, "", [ + 'Content-Length', + '0', + 'Server', + 'Microsoft-HTTPAPI/2.0', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'Date', + 'Tue, 02 Nov 2021 17:43:52 GMT' +]); diff --git a/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_invalid_file_delete.js b/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_invalid_file_delete.js new file mode 100644 index 000000000000..ba24187cda73 --- /dev/null +++ b/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_invalid_file_delete.js @@ -0,0 +1,18 @@ +let nock = require('nock'); + +module.exports.hash = "0d9469b3f72006adfc5ffce15953963a"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://endpoint', {"encodedQueryParams":true}) + .get('/v1/objects/0-wus-d4-ca4017a32f8514aa9f054f0917270000') + .reply(404, "", [ + 'Content-Length', + '0', + 'Server', + 'Microsoft-HTTPAPI/2.0', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'Date', + 'Tue, 02 Nov 2021 17:46:09 GMT' +]); diff --git a/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_unauthorized_delete.js b/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_unauthorized_delete.js new file mode 100644 index 000000000000..1305650d372d --- /dev/null +++ b/sdk/communication/communication-callingserver/recordings/node/delete_live_tests/recording_unauthorized_delete.js @@ -0,0 +1,18 @@ +let nock = require('nock'); + +module.exports.hash = "44aeeb9e1bce10c53420368854607266"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://endpoint', {"encodedQueryParams":true}) + .delete('/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05') + .reply(401, "", [ + 'Content-Length', + '0', + 'Server', + 'Microsoft-HTTPAPI/2.0', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'Date', + 'Tue, 02 Nov 2021 17:46:09 GMT' +]); diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index c5dc51dadbcc..0b083b676a09 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -887,6 +887,7 @@ function getDeleteOperationSpec(url: string, stringToSign: string): OperationSpe // Operation Specifications const serializer = new Serializer(Mappers, /* isXml */ false); const stringToSignHeader = CallingServerUtils.getStringToSignHeader(stringToSign); + const hostHeader = CallingServerUtils.getMsHostHeaders(stringToSign); const deleteOperationSpec: OperationSpec = { path: "", @@ -901,7 +902,7 @@ function getDeleteOperationSpec(url: string, stringToSign: string): OperationSpe requestBody: undefined, queryParameters: [], urlParameters: [], - headerParameters: [stringToSignHeader], + headerParameters: [stringToSignHeader, hostHeader], serializer }; diff --git a/sdk/communication/communication-callingserver/src/utils/utils.ts b/sdk/communication/communication-callingserver/src/utils/utils.ts index 0aa59ebc2fd3..ce74779fb9ba 100644 --- a/sdk/communication/communication-callingserver/src/utils/utils.ts +++ b/sdk/communication/communication-callingserver/src/utils/utils.ts @@ -31,4 +31,23 @@ export class CallingServerUtils { } }; } + + public static getMsHostHeaders( + hostName: string + ): OperationQueryParameter + { + const q = URLBuilder.parse(hostName!); + const hostAndPort = q.getHost()! + (q.getPort() !== undefined ? q.getPort() : ""); + return { + parameterPath: "x-ms-host", + mapper: { + defaultValue: hostAndPort, + isConstant: true, + serializedName: "x-ms-host", + type: { + name: "String" + } + } + } + } } diff --git a/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts b/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts index 7573df1ffd44..2292e041ac93 100644 --- a/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts +++ b/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts @@ -28,9 +28,8 @@ import { describe("Delete Live Tests", function() { let recorder: Recorder; - - const uri = - "https://endpoint/v1/objects/0-eus-d8-24d744599871098c7a22f28a4cb738d5"; + const uri = "https://endpoint/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05"; + const invalidUri = "https://endpoint/v1/objects/0-wus-d4-ca4017a32f8514aa9f054f0917270000"; const callingServerServiceClient = new CallingServerClient( env.COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING || @@ -53,13 +52,13 @@ import { this.skip(); } - const deleteRepsonse = await callingServerServiceClient.delete(uri); - assert.strictEqual(200, deleteRepsonse._response.status); + const result = await callingServerServiceClient.delete(uri); + assert.equal(200, result._response.status); }); it("unauthorized delete", async function(this: Context) { if (!isPlaybackMode()) { - // tslint:disable-next-line:no-invalid-this + // tslint:disable-next-l ine:no-invalid-this this.skip(); } @@ -78,12 +77,11 @@ import { // tslint:disable-next-line:no-invalid-this this.skip(); } - const invalidUri = "https://storage.asm.skype.com/v1/objects/0-eus-d3-00000000000000000000000000000000"; try { await callingServerServiceClient.download(invalidUri); } catch (e) { assert.equal((e as RestError).statusCode, 404); } }); -}); + }); From 6f55c5298ac6d4ff8746a5faf4bab353859b991d Mon Sep 17 00:00:00 2001 From: Melissa Neubert Date: Tue, 2 Nov 2021 12:25:08 -0700 Subject: [PATCH 3/6] PR feedback --- .../src/ContentDownloader.ts | 6 +- .../src/callingServerClient.ts | 55 +++++++++---------- .../src/utils/utils.ts | 10 ++++ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/sdk/communication/communication-callingserver/src/ContentDownloader.ts b/sdk/communication/communication-callingserver/src/ContentDownloader.ts index e2df1edfab5c..ea6d2d8cc0af 100644 --- a/sdk/communication/communication-callingserver/src/ContentDownloader.ts +++ b/sdk/communication/communication-callingserver/src/ContentDownloader.ts @@ -7,7 +7,6 @@ import * as Parameters from "./parameters"; import * as Mappers from "./generated/src/models/mappers"; import * as ExtraMappers from "./mappers"; import { CallingServerApiClientContext } from "./generated/src/callingServerApiClientContext"; -import { URLBuilder } from "@azure/core-http"; import { createSpan } from "./tracing"; import { SpanStatusCode } from "@azure/core-tracing"; @@ -64,10 +63,7 @@ export class ContentDownloader { const operationArguments: OperationArguments = { options: operationOptionsToRequestOptionsBase(options || {}) }; - - const q = URLBuilder.parse(contentUri); - const formattedUrl = q.getPath()!.startsWith("/") ? q.getPath()!.substr(1) : q.getPath()!; - const stringToSign = this.client.endpoint + formattedUrl; + const stringToSign = CallingServerUtils.getStringToSign(this.client.endpoint, contentUri); return this.client.sendOperationRequest( operationArguments, getDownloadContentOperationSpec(contentUri, stringToSign) diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index 0b083b676a09..f3ff46759ce3 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -74,7 +74,7 @@ import { serializeCallLocator } from "./callLocatorModelSerializer"; /** * Client options used to configure CallingServer Client API requests. */ -export interface CallingServerClientOptions extends PipelineOptions {} +export interface CallingServerClientOptions extends PipelineOptions { } /** * Checks whether the type of a value is CallingServerClientOptions or not. @@ -91,7 +91,7 @@ export class CallingServerClient { private readonly callingServerServiceClient: CallingServerApiClient; private readonly callConnectionRestClient: CallConnections; private readonly serverCallRestClient: ServerCalls; - private readonly deleteAndDownloadCallingServerApiClient: CallingServerApiClientContext; + private readonly storageApiClient: CallingServerApiClientContext; /** * Initializes a new instance of the CallingServerClient class. @@ -144,7 +144,7 @@ export class CallingServerClient { this.callingServerServiceClient = new CallingServerApiClient(url, pipeline); this.callConnectionRestClient = this.callingServerServiceClient.callConnections; this.serverCallRestClient = this.callingServerServiceClient.serverCalls; - this.deleteAndDownloadCallingServerApiClient = new CallingServerApiClientContext(url, pipeline); + this.storageApiClient = new CallingServerApiClientContext(url, pipeline); } /** @@ -156,7 +156,7 @@ export class CallingServerClient { } public initializeContentDownloader(): ContentDownloader { - return new ContentDownloader(this.deleteAndDownloadCallingServerApiClient); + return new ContentDownloader(this.storageApiClient); } /** @@ -831,50 +831,47 @@ export class CallingServerClient { } } - /** - * Deletes the content pointed to the uri passed as a parameter. - * - * * In Node.js, data returns ? - * * In browsers, data returns ? - * - * @param deleteUri - Endpoint where the content exists. - * - * Example usage (Node.js): - * - * ```js - * // Delete and convert a blob to a string - * const deleteUri = "https://deleteUri.com"; - * const deleteResponse = await callingServerClient.delete(deleteUri); - * - * ``` - */ + /** + * Deletes the content pointed to the uri passed as a parameter. + * + * * Returns a RestResponse indicating the result of the delete operation. + * + * @param deleteUri - Endpoint where the content exists. + * + * Example usage: + * + * ```js + * // Delete content + * const deleteUri = "https://deleteUri.com"; + * const deleteResponse = await callingServerClient.delete(deleteUri); + * + * ``` + */ public async delete( deleteUri: string, options: OperationOptions = {} - ): Promise - { + ): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-delete", options); const operationArguments: OperationArguments = { options: operationOptionsToRequestOptionsBase(updatedOptions) }; - try - { + try { const q = URLBuilder.parse(deleteUri); const formattedUrl = q.getPath()!.startsWith("/") ? q.getPath()!.substr(1) : q.getPath()!; - const stringToSign = this.deleteAndDownloadCallingServerApiClient.endpoint + formattedUrl; + const stringToSign = this.storageApiClient.endpoint + formattedUrl; - return this.deleteAndDownloadCallingServerApiClient.sendOperationRequest( + return this.storageApiClient.sendOperationRequest( operationArguments, getDeleteOperationSpec(deleteUri, stringToSign) ) as Promise - } catch(e) { + } catch (e) { span.setStatus({ code: SpanStatusCode.ERROR, message: e.message }); - throw e; + throw e; } finally { span.end(); } diff --git a/sdk/communication/communication-callingserver/src/utils/utils.ts b/sdk/communication/communication-callingserver/src/utils/utils.ts index ce74779fb9ba..b7118bc47b33 100644 --- a/sdk/communication/communication-callingserver/src/utils/utils.ts +++ b/sdk/communication/communication-callingserver/src/utils/utils.ts @@ -50,4 +50,14 @@ export class CallingServerUtils { } } } + + public static getStringToSign( + resourceEndpoint: string, + requestUri: string + ): string + { + const q = URLBuilder.parse(requestUri); + const formattedUrl = q.getPath()!.startsWith("/") ? q.getPath()!.substr(1) : q.getPath()!; + return resourceEndpoint + formattedUrl;; + } } From 09a8b337da358251eefd7cd4c58d58eededca0c0 Mon Sep 17 00:00:00 2001 From: Melissa Neubert Date: Tue, 2 Nov 2021 12:26:09 -0700 Subject: [PATCH 4/6] Fix formatting --- .../src/callingServerClient.ts | 41 ++--- .../src/utils/utils.ts | 20 +-- .../test/public/deleteContent.spec.ts | 159 +++++++++--------- 3 files changed, 102 insertions(+), 118 deletions(-) diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index f3ff46759ce3..bedaf8b7c0dc 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -74,7 +74,7 @@ import { serializeCallLocator } from "./callLocatorModelSerializer"; /** * Client options used to configure CallingServer Client API requests. */ -export interface CallingServerClientOptions extends PipelineOptions { } +export interface CallingServerClientOptions extends PipelineOptions {} /** * Checks whether the type of a value is CallingServerClientOptions or not. @@ -832,25 +832,22 @@ export class CallingServerClient { } /** - * Deletes the content pointed to the uri passed as a parameter. - * - * * Returns a RestResponse indicating the result of the delete operation. - * - * @param deleteUri - Endpoint where the content exists. - * - * Example usage: - * - * ```js - * // Delete content - * const deleteUri = "https://deleteUri.com"; - * const deleteResponse = await callingServerClient.delete(deleteUri); - * - * ``` - */ - public async delete( - deleteUri: string, - options: OperationOptions = {} - ): Promise { + * Deletes the content pointed to the uri passed as a parameter. + * + * * Returns a RestResponse indicating the result of the delete operation. + * + * @param deleteUri - Endpoint where the content exists. + * + * Example usage: + * + * ```js + * // Delete content + * const deleteUri = "https://deleteUri.com"; + * const deleteResponse = await callingServerClient.delete(deleteUri); + * + * ``` + */ + public async delete(deleteUri: string, options: OperationOptions = {}): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-delete", options); const operationArguments: OperationArguments = { @@ -865,7 +862,7 @@ export class CallingServerClient { return this.storageApiClient.sendOperationRequest( operationArguments, getDeleteOperationSpec(deleteUri, stringToSign) - ) as Promise + ) as Promise; } catch (e) { span.setStatus({ code: SpanStatusCode.ERROR, @@ -875,11 +872,9 @@ export class CallingServerClient { } finally { span.end(); } - } } - function getDeleteOperationSpec(url: string, stringToSign: string): OperationSpec { // Operation Specifications const serializer = new Serializer(Mappers, /* isXml */ false); diff --git a/sdk/communication/communication-callingserver/src/utils/utils.ts b/sdk/communication/communication-callingserver/src/utils/utils.ts index b7118bc47b33..dd72df897ae6 100644 --- a/sdk/communication/communication-callingserver/src/utils/utils.ts +++ b/sdk/communication/communication-callingserver/src/utils/utils.ts @@ -15,10 +15,7 @@ export class CallingServerUtils { return url.getScheme() === "http" || url.getScheme() === "https"; } - public static getStringToSignHeader( - stringToSign: string - ): OperationQueryParameter - { + public static getStringToSignHeader(stringToSign: string): OperationQueryParameter { return { parameterPath: "UriToSignWith", mapper: { @@ -32,10 +29,7 @@ export class CallingServerUtils { }; } - public static getMsHostHeaders( - hostName: string - ): OperationQueryParameter - { + public static getMsHostHeaders(hostName: string): OperationQueryParameter { const q = URLBuilder.parse(hostName!); const hostAndPort = q.getHost()! + (q.getPort() !== undefined ? q.getPort() : ""); return { @@ -48,16 +42,12 @@ export class CallingServerUtils { name: "String" } } - } + }; } - public static getStringToSign( - resourceEndpoint: string, - requestUri: string - ): string - { + public static getStringToSign(resourceEndpoint: string, requestUri: string): string { const q = URLBuilder.parse(requestUri); const formattedUrl = q.getPath()!.startsWith("/") ? q.getPath()!.substr(1) : q.getPath()!; - return resourceEndpoint + formattedUrl;; + return resourceEndpoint + formattedUrl; } } diff --git a/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts b/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts index 2292e041ac93..53b1128b0737 100644 --- a/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts +++ b/sdk/communication/communication-callingserver/test/public/deleteContent.spec.ts @@ -2,86 +2,85 @@ // Licensed under the MIT license. import { - env, - isPlaybackMode, - record, - Recorder, - RecorderEnvironmentSetup - } from "@azure-tools/test-recorder"; - import { assert } from "chai"; - import { CallingServerClient } from "../../src"; - import { Context } from "mocha"; - import { RestError } from "@azure/core-http"; - - const replaceableVariables: { [k: string]: string } = { - COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING: "endpoint=https://endpoint/;accesskey=banana" - }; - - const environmentSetup: RecorderEnvironmentSetup = { - replaceableVariables, - customizationsOnRecordings: [ - (recording: string): string => recording.replace(/(https:\/\/)([^/',]*)/, "$1endpoint"), - (recording: string): string => recording.replace("endpoint:443", "endpoint") - ], - queryParametersToSkip: [] - }; - - describe("Delete Live Tests", function() { - let recorder: Recorder; - const uri = "https://endpoint/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05"; - const invalidUri = "https://endpoint/v1/objects/0-wus-d4-ca4017a32f8514aa9f054f0917270000"; + env, + isPlaybackMode, + record, + Recorder, + RecorderEnvironmentSetup +} from "@azure-tools/test-recorder"; +import { assert } from "chai"; +import { CallingServerClient } from "../../src"; +import { Context } from "mocha"; +import { RestError } from "@azure/core-http"; - const callingServerServiceClient = new CallingServerClient( - env.COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING || - "endpoint=https://endpoint/;accesskey=banana" - ); - - beforeEach(async function(this: Context) { - recorder = record(this, environmentSetup); - }); - - afterEach(async function(this: Context) { - if (!this.currentTest?.isPending()) { - await recorder.stop(); - } - }); - - it("delete", async function(this: Context) { - if (!isPlaybackMode()) { - // tslint:disable-next-line:no-invalid-this - this.skip(); - } - - const result = await callingServerServiceClient.delete(uri); - assert.equal(200, result._response.status); - }); - - it("unauthorized delete", async function(this: Context) { - if (!isPlaybackMode()) { - // tslint:disable-next-l ine:no-invalid-this - this.skip(); - } - - try { - const unauthorizedCallingServerServiceClient = new CallingServerClient( - "endpoint=https://test.communication.azure.com/;accesskey=1234" - ) - await unauthorizedCallingServerServiceClient.delete(uri); - } catch (e) { - assert.equal((e as RestError).statusCode, 401); - } - }); +const replaceableVariables: { [k: string]: string } = { + COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING: "endpoint=https://endpoint/;accesskey=banana" +}; - it("invalid file delete", async function(this: Context) { - if (!isPlaybackMode()) { - // tslint:disable-next-line:no-invalid-this - this.skip(); - } - try { - await callingServerServiceClient.download(invalidUri); - } catch (e) { - assert.equal((e as RestError).statusCode, 404); - } - }); +const environmentSetup: RecorderEnvironmentSetup = { + replaceableVariables, + customizationsOnRecordings: [ + (recording: string): string => recording.replace(/(https:\/\/)([^/',]*)/, "$1endpoint"), + (recording: string): string => recording.replace("endpoint:443", "endpoint") + ], + queryParametersToSkip: [] +}; + +describe("Delete Live Tests", function() { + let recorder: Recorder; + const uri = "https://endpoint/v1/objects/0-wus-d6-fdf8ff0fdcd668bca8c52c0b1ee79b05"; + const invalidUri = "https://endpoint/v1/objects/0-wus-d4-ca4017a32f8514aa9f054f0917270000"; + + const callingServerServiceClient = new CallingServerClient( + env.COMMUNICATION_LIVETEST_DYNAMIC_CONNECTION_STRING || + "endpoint=https://endpoint/;accesskey=banana" + ); + + beforeEach(async function(this: Context) { + recorder = record(this, environmentSetup); + }); + + afterEach(async function(this: Context) { + if (!this.currentTest?.isPending()) { + await recorder.stop(); + } + }); + + it("delete", async function(this: Context) { + if (!isPlaybackMode()) { + // tslint:disable-next-line:no-invalid-this + this.skip(); + } + + const result = await callingServerServiceClient.delete(uri); + assert.equal(200, result._response.status); + }); + + it("unauthorized delete", async function(this: Context) { + if (!isPlaybackMode()) { + // tslint:disable-next-l ine:no-invalid-this + this.skip(); + } + + try { + const unauthorizedCallingServerServiceClient = new CallingServerClient( + "endpoint=https://test.communication.azure.com/;accesskey=1234" + ); + await unauthorizedCallingServerServiceClient.delete(uri); + } catch (e) { + assert.equal((e as RestError).statusCode, 401); + } + }); + + it("invalid file delete", async function(this: Context) { + if (!isPlaybackMode()) { + // tslint:disable-next-line:no-invalid-this + this.skip(); + } + try { + await callingServerServiceClient.download(invalidUri); + } catch (e) { + assert.equal((e as RestError).statusCode, 404); + } }); - +}); From 7696dc4d456acdfa3159be92fcaa3cc99f9a55e5 Mon Sep 17 00:00:00 2001 From: Melissa Neubert Date: Tue, 2 Nov 2021 13:45:44 -0700 Subject: [PATCH 5/6] PR updates --- .../src/callingServerClient.ts | 13 +++++++------ .../communication-callingserver/src/models.ts | 4 ++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index bedaf8b7c0dc..a44b0cc80356 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -17,7 +17,8 @@ import { PauseRecordingOptions, ResumeRecordingOptions, StopRecordingOptions, - GetRecordingPropertiesOptions + GetRecordingPropertiesOptions, + DeleteOptions } from "./models"; import { CallConnections, ServerCalls } from "./generated/src/operations"; import { @@ -847,7 +848,7 @@ export class CallingServerClient { * * ``` */ - public async delete(deleteUri: string, options: OperationOptions = {}): Promise { + public async delete(deleteUri: string, options: DeleteOptions = {}): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-delete", options); const operationArguments: OperationArguments = { @@ -855,10 +856,10 @@ export class CallingServerClient { }; try { - const q = URLBuilder.parse(deleteUri); - const formattedUrl = q.getPath()!.startsWith("/") ? q.getPath()!.substr(1) : q.getPath()!; - const stringToSign = this.storageApiClient.endpoint + formattedUrl; - + const stringToSign = CallingServerUtils.getStringToSign( + this.storageApiClient.endpoint, + deleteUri + ); return this.storageApiClient.sendOperationRequest( operationArguments, getDeleteOperationSpec(deleteUri, stringToSign) diff --git a/sdk/communication/communication-callingserver/src/models.ts b/sdk/communication/communication-callingserver/src/models.ts index 2d7df30f097a..04630834e3f6 100644 --- a/sdk/communication/communication-callingserver/src/models.ts +++ b/sdk/communication/communication-callingserver/src/models.ts @@ -131,6 +131,10 @@ export type StopRecordingOptions = OperationOptions; * Options to get recording properties. */ export type GetRecordingPropertiesOptions = OperationOptions; +/** + * Options to delete recording. + */ +export type DeleteOptions = OperationOptions; /** * Call Locator. From 707ceaaaad9efc43e9e10c241c9a8847f7df8a1b Mon Sep 17 00:00:00 2001 From: Melissa Neubert Date: Tue, 2 Nov 2021 13:58:39 -0700 Subject: [PATCH 6/6] Update api file --- .../review/communication-callingserver.api.md | 7 +++++-- .../communication-callingserver/src/callingServerClient.ts | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sdk/communication/communication-callingserver/review/communication-callingserver.api.md b/sdk/communication/communication-callingserver/review/communication-callingserver.api.md index 192749714745..0fa0d6f4709c 100644 --- a/sdk/communication/communication-callingserver/review/communication-callingserver.api.md +++ b/sdk/communication/communication-callingserver/review/communication-callingserver.api.md @@ -85,7 +85,7 @@ export class CallingServerClient { cancelMediaOperation(callLocator: CallLocator, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; cancelParticipantMediaOperation(callLocator: CallLocator, participant: CommunicationIdentifier, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; createCallConnection(source: CommunicationIdentifier, targets: CommunicationIdentifier[], options: CreateCallConnectionOptions): Promise; - delete(deleteUri: string, options?: OperationOptions): Promise; + delete(deleteUri: string, options?: DeleteOptions): Promise; download(uri: string, offset?: number, options?: DownloadOptions): Promise; getCallConnection(callConnectionId: string): CallConnection; // Warning: (ae-forgotten-export) The symbol "CallRecordingProperties" needs to be exported by the entry point index.d.ts @@ -103,7 +103,7 @@ export class CallingServerClient { // Warning: (ae-forgotten-export) The symbol "StartCallRecordingResult" needs to be exported by the entry point index.d.ts startRecording(callLocator: CallLocator, recordingStateCallbackUri: string, options?: StartRecordingOptions): Promise; stopRecording(recordingId: string, options?: StopRecordingOptions): Promise; -} + } // @public export interface CallingServerClientOptions extends PipelineOptions { @@ -165,6 +165,9 @@ export interface CreateCallConnectionOptions extends OperationOptions { subject?: string; } +// @public +export type DeleteOptions = OperationOptions; + // @public (undocumented) export interface DownloadContentOptions extends DownloadOptions { range?: string; diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index a44b0cc80356..80bfd83f7568 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -54,10 +54,8 @@ import { operationOptionsToRequestOptionsBase, RestResponse, OperationArguments, - OperationOptions, OperationSpec, - Serializer, - URLBuilder + Serializer } from "@azure/core-http"; import { SpanStatusCode } from "@azure/core-tracing"; import { CallingServerApiClient } from "./generated/src/callingServerApiClient";