From 8874f49db9c348cb496e2a607c543803b53209d6 Mon Sep 17 00:00:00 2001 From: Fabian Meiswinkel Date: Fri, 5 Aug 2022 01:14:29 +0000 Subject: [PATCH 1/4] Fixing id encoding issues when using special characters for customers already on ComputeGateway --- .../steps/cosmos-integration-public.yml | 2 + sdk/cosmosdb/cosmos/src/client/Item/Item.ts | 26 +- sdk/cosmosdb/cosmos/src/common/helper.ts | 22 +- sdk/cosmosdb/cosmos/src/common/uriFactory.ts | 4 +- sdk/cosmosdb/cosmos/src/utils/headers.ts | 16 +- .../cosmos/test/public/common/TestHelpers.ts | 11 +- .../public/functional/itemIdEncoding.spec.ts | 341 +++++++++++++++--- 7 files changed, 339 insertions(+), 83 deletions(-) diff --git a/eng/pipelines/templates/steps/cosmos-integration-public.yml b/eng/pipelines/templates/steps/cosmos-integration-public.yml index 640174a17217..3477af9aa1db 100644 --- a/eng/pipelines/templates/steps/cosmos-integration-public.yml +++ b/eng/pipelines/templates/steps/cosmos-integration-public.yml @@ -3,3 +3,5 @@ parameters: steps: - template: /eng/common/pipelines/templates/steps/cosmos-emulator.yml + parameters: + StartParameters: '/noexplorer /noui /enablepreview /EnableSqlComputeEndpoint /disableratelimiting /enableaadauthentication /partitioncount=50 diff --git a/sdk/cosmosdb/cosmos/src/client/Item/Item.ts b/sdk/cosmosdb/cosmos/src/client/Item/Item.ts index 60e3b0357b82..d402f3687cc1 100644 --- a/sdk/cosmosdb/cosmos/src/client/Item/Item.ts +++ b/sdk/cosmosdb/cosmos/src/client/Item/Item.ts @@ -29,11 +29,7 @@ export class Item { * Returns a reference URL to the resource. Used for linking in Permissions. */ public get url(): string { - return createDocumentUri( - this.container.database.id, - this.container.id, - encodeURIComponent(this.id) - ); + return createDocumentUri(this.container.database.id, this.container.id, this.id); } /** @@ -84,9 +80,8 @@ export class Item { this.partitionKey = undefinedPartitionKey(partitionKeyDefinition); } - const resourceUri: string = this.url; - const path = getPathFromLink(resourceUri); - const id = getIdFromLink(resourceUri); + const path = getPathFromLink(this.url); + const id = getIdFromLink(this.url); let response: Response; try { response = await this.clientContext.read({ @@ -154,9 +149,8 @@ export class Item { throw err; } - const resourceUri: string = this.url; - const path = getPathFromLink(resourceUri); - const id = getIdFromLink(resourceUri); + const path = getPathFromLink(this.url); + const id = getIdFromLink(this.url); const response = await this.clientContext.replace({ body, @@ -192,9 +186,8 @@ export class Item { this.partitionKey = undefinedPartitionKey(partitionKeyDefinition); } - const resourceUri: string = this.url; - const path = getPathFromLink(resourceUri); - const id = getIdFromLink(resourceUri); + const path = getPathFromLink(this.url); + const id = getIdFromLink(this.url); const response = await this.clientContext.delete({ path, @@ -230,9 +223,8 @@ export class Item { this.partitionKey = extractPartitionKey(body, partitionKeyDefinition); } - const resourceUri: string = this.url; - const path = getPathFromLink(resourceUri); - const id = getIdFromLink(resourceUri); + const path = getPathFromLink(this.url); + const id = getIdFromLink(this.url); const response = await this.clientContext.patch({ body, diff --git a/sdk/cosmosdb/cosmos/src/common/helper.ts b/sdk/cosmosdb/cosmos/src/common/helper.ts index 814d9b7946cd..5e5e303183f0 100644 --- a/sdk/cosmosdb/cosmos/src/common/helper.ts +++ b/sdk/cosmosdb/cosmos/src/common/helper.ts @@ -6,6 +6,7 @@ import { OperationType, ResourceType } from "./constants"; const trimLeftSlashes = new RegExp("^[/]+"); const trimRightSlashes = new RegExp("[/]+$"); const illegalResourceIdCharacters = new RegExp("[/\\\\?#]"); +const illegalItemResourceIdCharacters = new RegExp("[/\\\\#]"); /** @hidden */ export function jsonStringifyAndEscapeNonASCII(arg: unknown): string { @@ -223,7 +224,7 @@ export function isItemResourceValid(resource: { id?: string }, err: { message?: return false; } - if (resource.id.indexOf("/") !== -1 || resource.id.indexOf("\\") !== -1) { + if (resource.id.indexOf("/") !== -1 || resource.id.indexOf("\\") !== -1 || resource.id.indexOf("#") !== -1) { err.message = "Id contains illegal chars."; return false; } @@ -277,7 +278,24 @@ export function validateResourceId(resourceId: string): boolean { // if resource id contains illegal characters throw an error if (illegalResourceIdCharacters.test(resourceId)) { - throw new Error("Illegal characters ['/', '\\'] cannot be used in resourceId"); + throw new Error("Illegal characters ['/', '\\', '#', '?'] cannot be used in resourceId"); + } + + return true; +} + +/** + * @hidden + */ + export function validateItemResourceId(resourceId: string): boolean { + // if resourceId is not a string or is empty throw an error + if (typeof resourceId !== "string" || isStringNullOrEmpty(resourceId)) { + throw new Error("Resource Id must be a string and cannot be undefined, null or empty"); + } + + // if resource id contains illegal characters throw an error + if (illegalItemResourceIdCharacters.test(resourceId)) { + throw new Error("Illegal characters ['/', '\\', '#'] cannot be used in resourceId"); } return true; diff --git a/sdk/cosmosdb/cosmos/src/common/uriFactory.ts b/sdk/cosmosdb/cosmos/src/common/uriFactory.ts index 3b12b1a91821..b988730983e9 100644 --- a/sdk/cosmosdb/cosmos/src/common/uriFactory.ts +++ b/sdk/cosmosdb/cosmos/src/common/uriFactory.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import { Constants } from "./constants"; -import { trimSlashFromLeftAndRight, validateResourceId } from "./helper"; +import { trimSlashFromLeftAndRight, validateResourceId, validateItemResourceId } from "./helper"; /** * Would be used when creating or deleting a DocumentCollection @@ -74,7 +74,7 @@ export function createDocumentUri( documentId: string ): string { documentId = trimSlashFromLeftAndRight(documentId); - validateResourceId(documentId); + validateItemResourceId(documentId); return ( createDocumentCollectionUri(databaseId, collectionId) + diff --git a/sdk/cosmosdb/cosmos/src/utils/headers.ts b/sdk/cosmosdb/cosmos/src/utils/headers.ts index 349c98820fed..b8407d28bc3c 100644 --- a/sdk/cosmosdb/cosmos/src/utils/headers.ts +++ b/sdk/cosmosdb/cosmos/src/utils/headers.ts @@ -27,20 +27,6 @@ export async function generateHeaders( }; } -function getEffectiveResourceIdForSignature(resourceId: string) { - const lastSlashPosition = resourceId.lastIndexOf("/"); - if (lastSlashPosition <= 0) { - return resourceId; - } - - const prefix: string = resourceId.substring(0, lastSlashPosition); - if (!prefix.endsWith("/docs")) { - return resourceId; - } - - return prefix + "/" + decodeURIComponent(resourceId.substring(lastSlashPosition + 1)); -} - async function signature( masterKey: string, method: HTTPMethod, @@ -55,7 +41,7 @@ async function signature( "\n" + resourceType.toLowerCase() + "\n" + - getEffectiveResourceIdForSignature(resourceId) + + resourceId + "\n" + date.toUTCString().toLowerCase() + "\n" + diff --git a/sdk/cosmosdb/cosmos/test/public/common/TestHelpers.ts b/sdk/cosmosdb/cosmos/test/public/common/TestHelpers.ts index bef303700085..388992e35b66 100644 --- a/sdk/cosmosdb/cosmos/test/public/common/TestHelpers.ts +++ b/sdk/cosmosdb/cosmos/test/public/common/TestHelpers.ts @@ -18,12 +18,21 @@ import { masterKey } from "../common/_fakeTestSecrets"; import { DatabaseRequest } from "../../../src"; import { ContainerRequest } from "../../../src"; -const defaultClient = new CosmosClient({ +const defaultRoutingGatewayPort: string = ":8081"; +const defaultComputeGatewayPort: string = ":8903"; + +export const defaultClient = new CosmosClient({ endpoint, key: masterKey, connectionPolicy: { enableBackgroundEndpointRefreshing: false }, }); +export const defaultComputeGatewayClient = new CosmosClient({ + endpoint: endpoint.replace(defaultRoutingGatewayPort, defaultComputeGatewayPort), + key: masterKey, + connectionPolicy: { enableBackgroundEndpointRefreshing: false }, +}); + export function addEntropy(name: string): string { return name + getEntropy(); } diff --git a/sdk/cosmosdb/cosmos/test/public/functional/itemIdEncoding.spec.ts b/sdk/cosmosdb/cosmos/test/public/functional/itemIdEncoding.spec.ts index d5423401a9b6..2bc91156eab9 100644 --- a/sdk/cosmosdb/cosmos/test/public/functional/itemIdEncoding.spec.ts +++ b/sdk/cosmosdb/cosmos/test/public/functional/itemIdEncoding.spec.ts @@ -2,8 +2,8 @@ // Licensed under the MIT license. import assert from "assert"; import { Suite } from "mocha"; -import { Container } from "../../../src"; -import { getTestContainer, removeAllDatabases } from "../common/TestHelpers"; +import { Container, CosmosClient } from "../../../src"; +import { getTestContainer, removeAllDatabases, defaultClient, defaultComputeGatewayClient } from "../common/TestHelpers"; interface ItemPayload { id?: string; @@ -27,8 +27,13 @@ const createPayload = function (id: string): ItemPayload { }; }; -const executeTestCase = async function (scenario: TestScenario) { - const container: Container = await getTestContainer(scenario.name, undefined, { +const executeTestCaseOnComputeGateway = async function (scenario: TestScenario) { + return executeTestCase(scenario, true); +} + +const executeTestCase = async function (scenario: TestScenario, useComputeGateway: boolean = false) { + const client: CosmosClient = useComputeGateway ? defaultComputeGatewayClient : defaultClient + const container: Container = await getTestContainer(scenario.name, client, { partitionKey: { paths: ["/pk"], version: undefined, @@ -110,9 +115,35 @@ describe("Id encoding", function (this: Suite) { await removeAllDatabases(); }); - it("plainVanillaId", async function () { + it("RGW_plainVanillaId", async function () { + const scenario: TestScenario = { + name: "RGW_PlainVanillaId", + id: "Test", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + + await executeTestCase(scenario); + }); + + it("CGW_plainVanillaId", async function () { + const scenario: TestScenario = { + name: "CGW_PlainVanillaId", + id: "Test", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_ContainerIdWithUnicode鱀", async function () { const scenario: TestScenario = { - name: "PlainVanillaId", + name: "RGW_ContainerIdWithUnicode鱀", id: "Test", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -123,9 +154,9 @@ describe("Id encoding", function (this: Suite) { await executeTestCase(scenario); }); - it("ContainerIdWithUnicode鱀", async function () { + it("CGW_ContainerIdWithUnicode鱀", async function () { const scenario: TestScenario = { - name: "ContainerIdWithUnicode鱀", + name: "CGW_ContainerIdWithUnicode鱀", id: "Test", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -133,12 +164,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithWhitespaces", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithWhitespaces", + id: "This is a test", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idWithWhitespaces", async function () { + it("CGW_idWithWhitespaces", async function () { const scenario: TestScenario = { - name: "IdWithWhitespaces", + name: "CGW_IdWithWhitespaces", id: "This is a test", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -146,12 +190,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idStartingWithWhitespace", async function () { + const scenario: TestScenario = { + name: "RGW_IdStartingWithWhitespace", + id: " Test", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idStartingWithWhitespace", async function () { + it("CGW_idStartingWithWhitespace", async function () { const scenario: TestScenario = { - name: "IdStartingWithWhitespace", + name: "CGW_IdStartingWithWhitespace", id: " Test", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -159,12 +216,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idStartingWithWhitespaces", async function () { + const scenario: TestScenario = { + name: "RGW_IdStartingWithWhitespaces", + id: " Test", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idStartingWithWhitespaces", async function () { + it("CGW_idStartingWithWhitespaces", async function () { const scenario: TestScenario = { - name: "IdStartingWithWhitespaces", + name: "CGW_IdStartingWithWhitespaces", id: " Test", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -172,12 +242,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idEndingWithWhitespace", async function () { + const scenario: TestScenario = { + name: "RGW_IdEndingWithWhitespace", + id: "Test ", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 401, + expectedReplaceStatusCode: 401, + expectedDeleteStatusCode: 401, + }; + await executeTestCase(scenario); }); - it("idEndingWithWhitespace", async function () { + it("CGW_idEndingWithWhitespace", async function () { const scenario: TestScenario = { - name: "IdEndingWithWhitespace", + name: "CGW_IdEndingWithWhitespace", id: "Test ", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -185,12 +268,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idEndingWithWhitespaces", async function () { + const scenario: TestScenario = { + name: "RGW_IdEndingWithWhitespaces", + id: "Test ", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 401, + expectedReplaceStatusCode: 401, + expectedDeleteStatusCode: 401, + }; + await executeTestCase(scenario); }); - it("idEndingWithWhitespaces", async function () { + it("CGW_idEndingWithWhitespaces", async function () { const scenario: TestScenario = { - name: "IdEndingWithWhitespaces", + name: "CGW_IdEndingWithWhitespaces", id: "Test ", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -198,12 +294,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithUnicodeCharacters", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithUnicodeCharacters", + id: "WithUnicode鱀", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idWithUnicodeCharacters", async function () { + it("CGW_idWithUnicodeCharacters", async function () { const scenario: TestScenario = { - name: "IdWithUnicodeCharacters", + name: "CGW_IdWithUnicodeCharacters", id: "WithUnicode鱀", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -211,12 +320,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithAllowedSpecialCharacters", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithAllowedSpecialCharacters", + id: "WithAllowedSpecial,=.:~+-@()^${}[]!_Chars", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idWithAllowedSpecialCharacters", async function () { + it("CGW_idWithAllowedSpecialCharacters", async function () { const scenario: TestScenario = { - name: "IdWithAllowedSpecialCharacters", + name: "CGW_IdWithAllowedSpecialCharacters", id: "WithAllowedSpecial,=.:~+-@()^${}[]!_Chars", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -224,17 +346,35 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithBase64EncodedIdCharacters", async function () { + const base64EncodedId = + "BQE1D3PdG4N4bzU9TKaCIM3qc0TVcZ2/Y3jnsRfwdHC1ombkX3F1dot/SG0/UTq9AbgdX3" + + "kOWoP6qL6lJqWeKgV3zwWWPZO/t5X0ehJzv9LGkWld07LID2rhWhGT6huBM6Q="; + const safeBase64EncodedId = base64EncodedId.replace(/\//g, "-"); + + const scenario: TestScenario = { + name: "RGW_IdWithBase64EncodedIdCharacters", + id: safeBase64EncodedId, + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idWithBase64EncodedIdCharacters", async function () { + it("CGW_idWithBase64EncodedIdCharacters", async function () { const base64EncodedId = "BQE1D3PdG4N4bzU9TKaCIM3qc0TVcZ2/Y3jnsRfwdHC1ombkX3F1dot/SG0/UTq9AbgdX3" + "kOWoP6qL6lJqWeKgV3zwWWPZO/t5X0ehJzv9LGkWld07LID2rhWhGT6huBM6Q="; const safeBase64EncodedId = base64EncodedId.replace(/\//g, "-"); const scenario: TestScenario = { - name: "IdWithBase64EncodedIdCharacters", + name: "CGW_IdWithBase64EncodedIdCharacters", id: safeBase64EncodedId, expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -242,12 +382,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idEndingWithPercentEncodedWhitespace", async function () { + const scenario: TestScenario = { + name: "RGW_IdEndingWithPercentEncodedWhitespace", + id: "IdEndingWithPercentEncodedWhitespace%20", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 401, + expectedReplaceStatusCode: 401, + expectedDeleteStatusCode: 401, + }; + await executeTestCase(scenario); }); - it("idEndingWithPercentEncodedWhitespace", async function () { + it("CGW_idEndingWithPercentEncodedWhitespace", async function () { const scenario: TestScenario = { - name: "IdEndingWithPercentEncodedWhitespace", + name: "CGW_IdEndingWithPercentEncodedWhitespace", id: "IdEndingWithPercentEncodedWhitespace%20", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -255,12 +408,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithPercentEncodedSpecialChar", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithPercentEncodedSpecialChar", + id: "WithPercentEncodedSpecialChar%E9%B1%80", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 401, + expectedReplaceStatusCode: 401, + expectedDeleteStatusCode: 401, + }; + await executeTestCase(scenario); }); - it("idWithPercentEncodedSpecialChar", async function () { + it("CGW_idWithPercentEncodedSpecialChar", async function () { const scenario: TestScenario = { - name: "IdWithPercentEncodedSpecialChar", + name: "CGW_IdWithPercentEncodedSpecialChar", id: "WithPercentEncodedSpecialChar%E9%B1%80", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -268,12 +434,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithDisallowedCharQuestionMark", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithDisallowedCharQuestionMark", + id: "Disallowed?Chars", + expectedCreateStatusCode: 201, + expectedReadStatusCode: 200, + expectedReplaceStatusCode: 200, + expectedDeleteStatusCode: 204, + }; + await executeTestCase(scenario); }); - it("idWithDisallowedCharQuestionMark", async function () { + it("CGW_idWithDisallowedCharQuestionMark", async function () { const scenario: TestScenario = { - name: "IdWithDisallowedCharQuestionMark", + name: "CGW_IdWithDisallowedCharQuestionMark", id: "Disallowed?Chars", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -281,47 +460,91 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithDisallowedCharForwardSlash", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithDisallowedCharForwardSlash", + id: "Disallowed/Chars", + expectedCreateStatusCode: 400, + expectedCreateErrorMessage: "Id contains illegal chars.", + }; + await executeTestCase(scenario); }); - it("idWithDisallowedCharForwardSlash", async function () { + it("CGW_idWithDisallowedCharForwardSlash", async function () { const scenario: TestScenario = { - name: "IdWithDisallowedCharForwardSlash", + name: "CGW_IdWithDisallowedCharForwardSlash", id: "Disallowed/Chars", expectedCreateStatusCode: 400, expectedCreateErrorMessage: "Id contains illegal chars.", }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithDisallowedCharBackSlash", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithDisallowedCharBackSlash", + id: "Disallowed\\Chars", + expectedCreateStatusCode: 400, + expectedCreateErrorMessage: "Id contains illegal chars.", + }; + await executeTestCase(scenario); }); - it("idWithDisallowedCharBackSlash", async function () { + it("CGW_idWithDisallowedCharBackSlash", async function () { const scenario: TestScenario = { - name: "IdWithDisallowedCharBackSlash", + name: "CGW_IdWithDisallowedCharBackSlash", id: "Disallowed\\Chars", expectedCreateStatusCode: 400, expectedCreateErrorMessage: "Id contains illegal chars.", }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithDisallowedCharPoundSign", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithDisallowedCharPoundSign", + id: "Disallowed#Chars", + expectedCreateStatusCode: 400, + expectedCreateErrorMessage: "Id contains illegal chars.", + }; + await executeTestCase(scenario); }); - it("idWithDisallowedCharPoundSign", async function () { + it("CGW_idWithDisallowedCharPoundSign", async function () { const scenario: TestScenario = { - name: "IdWithDisallowedCharPoundSign", + name: "CGW_IdWithDisallowedCharPoundSign", id: "Disallowed#Chars", + expectedCreateStatusCode: 400, + expectedCreateErrorMessage: "Id contains illegal chars.", + }; + + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithCarriageReturn", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithCarriageReturn", + id: "With\rCarriageReturn", expectedCreateStatusCode: 201, - expectedReadStatusCode: 200, - expectedReplaceStatusCode: 200, - expectedDeleteStatusCode: 204, + expectedReadStatusCode: undefined, + expectedReplaceStatusCode: undefined, + expectedDeleteStatusCode: undefined, }; await executeTestCase(scenario); }); - it("idWithCarriageReturn", async function () { + it("CGW_idWithCarriageReturn", async function () { const scenario: TestScenario = { - name: "IdWithCarriageReturn", + name: "CGW_IdWithCarriageReturn", id: "With\rCarriageReturn", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -329,12 +552,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithTab", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithTab", + id: "With\tTab", + expectedCreateStatusCode: 201, + expectedReadStatusCode: undefined, + expectedReplaceStatusCode: undefined, + expectedDeleteStatusCode: undefined, + }; + await executeTestCase(scenario); }); - it("idWithTab", async function () { + it("CGW_idWithTab", async function () { const scenario: TestScenario = { - name: "IdWithTab", + name: "CGW_IdWithTab", id: "With\tTab", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -342,12 +578,25 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; + await executeTestCaseOnComputeGateway(scenario); + }); + + it("RGW_idWithLineFeed", async function () { + const scenario: TestScenario = { + name: "RGW_IdWithLineFeed", + id: "With\nLineFeed", + expectedCreateStatusCode: 201, + expectedReadStatusCode: undefined, + expectedReplaceStatusCode: undefined, + expectedDeleteStatusCode: undefined, + }; + await executeTestCase(scenario); }); - it("idWithLineFeed", async function () { + it("CGW_idWithLineFeed", async function () { const scenario: TestScenario = { - name: "IdWithLineFeed", + name: "CGW_IdWithLineFeed", id: "With\nLineFeed", expectedCreateStatusCode: 201, expectedReadStatusCode: 200, @@ -355,6 +604,6 @@ describe("Id encoding", function (this: Suite) { expectedDeleteStatusCode: 204, }; - await executeTestCase(scenario); + await executeTestCaseOnComputeGateway(scenario); }); }); From 1e12f37e85e97467d2fbd733718d0067faca4b3a Mon Sep 17 00:00:00 2001 From: Fabian Meiswinkel Date: Fri, 5 Aug 2022 01:20:46 +0000 Subject: [PATCH 2/4] Update CHANGELOG.md --- sdk/cosmosdb/cosmos/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/cosmosdb/cosmos/CHANGELOG.md b/sdk/cosmosdb/cosmos/CHANGELOG.md index 959ca130d102..5eed2db7680b 100644 --- a/sdk/cosmosdb/cosmos/CHANGELOG.md +++ b/sdk/cosmosdb/cosmos/CHANGELOG.md @@ -7,6 +7,7 @@ ### Breaking Changes ### Bugs Fixed +- Revertes changes of [PR 22548](https://github.com/Azure/azure-sdk-for-js/pull/22548) to avoid possible regression when customers use id with special characters and their account is on ComputeGateway already. - See [PR 22818](https://github.com/Azure/azure-sdk-for-js/pull/22818) ### Other Changes From c3154c745d977efa05eabd76b01bfae0672410c3 Mon Sep 17 00:00:00 2001 From: Fabian Meiswinkel Date: Fri, 5 Aug 2022 08:42:23 +0000 Subject: [PATCH 3/4] Update CHANGELOG.md --- sdk/cosmosdb/cosmos/CHANGELOG.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sdk/cosmosdb/cosmos/CHANGELOG.md b/sdk/cosmosdb/cosmos/CHANGELOG.md index 5eed2db7680b..6b25a7110749 100644 --- a/sdk/cosmosdb/cosmos/CHANGELOG.md +++ b/sdk/cosmosdb/cosmos/CHANGELOG.md @@ -1,16 +1,10 @@ # Release History -## 3.16.4 (Unreleased) - -### Features Added - -### Breaking Changes +## 3.16.4 (2022-08-05) ### Bugs Fixed - Revertes changes of [PR 22548](https://github.com/Azure/azure-sdk-for-js/pull/22548) to avoid possible regression when customers use id with special characters and their account is on ComputeGateway already. - See [PR 22818](https://github.com/Azure/azure-sdk-for-js/pull/22818) -### Other Changes - ## 3.16.3 (2022-07-13) ### Bugs Fixed From 58f93e197875db1a8164232a42554def876b9413 Mon Sep 17 00:00:00 2001 From: Fabian Meiswinkel Date: Fri, 5 Aug 2022 10:37:11 +0000 Subject: [PATCH 4/4] Fixing typos --- sdk/cosmosdb/cosmos/CHANGELOG.md | 2 +- sdk/cosmosdb/cosmos/src/common/helper.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/cosmosdb/cosmos/CHANGELOG.md b/sdk/cosmosdb/cosmos/CHANGELOG.md index 6b25a7110749..81d462c80fe7 100644 --- a/sdk/cosmosdb/cosmos/CHANGELOG.md +++ b/sdk/cosmosdb/cosmos/CHANGELOG.md @@ -3,7 +3,7 @@ ## 3.16.4 (2022-08-05) ### Bugs Fixed -- Revertes changes of [PR 22548](https://github.com/Azure/azure-sdk-for-js/pull/22548) to avoid possible regression when customers use id with special characters and their account is on ComputeGateway already. - See [PR 22818](https://github.com/Azure/azure-sdk-for-js/pull/22818) +- Reverts changes of [PR 22548](https://github.com/Azure/azure-sdk-for-js/pull/22548) to avoid possible regression when customers use id with special characters and their account is on ComputeGateway already. - See [PR 22818](https://github.com/Azure/azure-sdk-for-js/pull/22818) ## 3.16.3 (2022-07-13) diff --git a/sdk/cosmosdb/cosmos/src/common/helper.ts b/sdk/cosmosdb/cosmos/src/common/helper.ts index 5e5e303183f0..84b6044cfb0f 100644 --- a/sdk/cosmosdb/cosmos/src/common/helper.ts +++ b/sdk/cosmosdb/cosmos/src/common/helper.ts @@ -273,12 +273,12 @@ export function trimSlashFromLeftAndRight(inputString: string): string { export function validateResourceId(resourceId: string): boolean { // if resourceId is not a string or is empty throw an error if (typeof resourceId !== "string" || isStringNullOrEmpty(resourceId)) { - throw new Error("Resource Id must be a string and cannot be undefined, null or empty"); + throw new Error("Resource ID must be a string and cannot be undefined, null or empty"); } // if resource id contains illegal characters throw an error if (illegalResourceIdCharacters.test(resourceId)) { - throw new Error("Illegal characters ['/', '\\', '#', '?'] cannot be used in resourceId"); + throw new Error("Illegal characters ['/', '\\', '#', '?'] cannot be used in Resource ID"); } return true; @@ -290,12 +290,12 @@ export function validateResourceId(resourceId: string): boolean { export function validateItemResourceId(resourceId: string): boolean { // if resourceId is not a string or is empty throw an error if (typeof resourceId !== "string" || isStringNullOrEmpty(resourceId)) { - throw new Error("Resource Id must be a string and cannot be undefined, null or empty"); + throw new Error("Resource ID must be a string and cannot be undefined, null or empty"); } // if resource id contains illegal characters throw an error if (illegalItemResourceIdCharacters.test(resourceId)) { - throw new Error("Illegal characters ['/', '\\', '#'] cannot be used in resourceId"); + throw new Error("Illegal characters ['/', '\\', '#'] cannot be used in Resource ID"); } return true;