diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index e32351077a..de19703a04 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -229,10 +229,10 @@ importers: ../../packages/typespec-ts: specifiers: '@azure-rest/core-client': ^2.1.0 - '@azure-tools/cadl-ranch': ^0.14.3 + '@azure-tools/cadl-ranch': ^0.14.5 '@azure-tools/cadl-ranch-api': ^0.4.6 - '@azure-tools/cadl-ranch-expect': ^0.15.1 - '@azure-tools/cadl-ranch-specs': ^0.36.1 + '@azure-tools/cadl-ranch-expect': ^0.15.3 + '@azure-tools/cadl-ranch-specs': ^0.37.1 '@azure-tools/rlc-common': workspace:^0.33.0 '@azure-tools/typespec-autorest': '>=0.45.0 <1.0.0' '@azure-tools/typespec-azure-core': '>=0.45.0 <1.0.0' @@ -288,10 +288,10 @@ importers: tslib: 2.6.2 devDependencies: '@azure-rest/core-client': 2.1.0 - '@azure-tools/cadl-ranch': 0.14.3_e53zy5pp4bbrxbruosuhuay6sm + '@azure-tools/cadl-ranch': 0.14.5_e53zy5pp4bbrxbruosuhuay6sm '@azure-tools/cadl-ranch-api': 0.4.6 - '@azure-tools/cadl-ranch-expect': 0.15.1_y3yxhtjg35d3g6m2dacugmsjfu - '@azure-tools/cadl-ranch-specs': 0.36.1_cvode6xhpuyl3aj5dk75xzx5nu + '@azure-tools/cadl-ranch-expect': 0.15.3_y3yxhtjg35d3g6m2dacugmsjfu + '@azure-tools/cadl-ranch-specs': 0.37.1_rlqyi2csgeth45ysk6yolt4cci '@azure-tools/typespec-autorest': 0.45.0_ch4e7dmaodljkmzs7h7dd2lznu '@azure-tools/typespec-azure-core': 0.45.0_alrznhyrw66bvx6gp4zrtdfkte '@azure-tools/typespec-azure-resource-manager': 0.45.0_rxzquxnvxkwebgrumlsfc6ni5i @@ -429,8 +429,8 @@ packages: - '@types/express' dev: true - /@azure-tools/cadl-ranch-coverage-sdk/0.8.3: - resolution: {integrity: sha512-tYgSawBWKRMwaA8sW4CuVynMlxnxmwjH0pG3qkHDuX8oIxvi/yawrLimLDNV29zAQiESHJkFU7THh/qG51oYPQ==} + /@azure-tools/cadl-ranch-coverage-sdk/0.8.4: + resolution: {integrity: sha512-N207EZEdJrXDKUVmi5Cnw/4y+/Ou9dTbdhMPDoLaalUxZp8T/YK+Y057/M88G0dY76PEAwWPPDolLchW62LZNQ==} engines: {node: '>=16.0.0'} dependencies: '@azure/identity': 4.4.1 @@ -440,8 +440,8 @@ packages: - supports-color dev: true - /@azure-tools/cadl-ranch-expect/0.15.1_y3yxhtjg35d3g6m2dacugmsjfu: - resolution: {integrity: sha512-gVu077Rtry3YM0JvsMocXi2KMQPUBaMSF7BKiIKzsxIB/YKAYKe+WEAMfkbFs3BQrbbYMMEWSMQQVUFzfaWpog==} + /@azure-tools/cadl-ranch-expect/0.15.3_y3yxhtjg35d3g6m2dacugmsjfu: + resolution: {integrity: sha512-ulUf2aN9UznF71NMwqVjcvEOw3F5BlL1HqeTwHZl3ZgRs8x2+HRLE+lwIEjfQi6h1ISn9u3kr+wslB03uOaoIQ==} engines: {node: '>=16.0.0'} peerDependencies: '@typespec/compiler': ~0.59.0 @@ -455,11 +455,11 @@ packages: '@typespec/versioning': 0.59.0_@typespec+compiler@0.59.0 dev: true - /@azure-tools/cadl-ranch-specs/0.36.1_cvode6xhpuyl3aj5dk75xzx5nu: - resolution: {integrity: sha512-DmgSaMeEhM6N3ZTFCDcBAy73hz8o1PYUdAKCN9P6WCj8sOVzZ8FCD9CGi2XjKBMqeJr4KHHooIgceglODJWSwQ==} + /@azure-tools/cadl-ranch-specs/0.37.1_rlqyi2csgeth45ysk6yolt4cci: + resolution: {integrity: sha512-XR8UxsbTQTSYbgyObcytRP0PLNWrU6cA8dTwQYh+VA/92HrSQYaJ8cQZZ/EyIFjFuSEVGQ74Rx6hpGvfKUrh2w==} engines: {node: '>=16.0.0'} peerDependencies: - '@azure-tools/cadl-ranch-expect': ~0.15.1 + '@azure-tools/cadl-ranch-expect': ~0.15.3 '@azure-tools/typespec-azure-core': ~0.45.0 '@typespec/compiler': ~0.59.0 '@typespec/http': ~0.59.0 @@ -467,9 +467,9 @@ packages: '@typespec/versioning': ~0.59.0 '@typespec/xml': ~0.59.0 dependencies: - '@azure-tools/cadl-ranch': 0.14.3_e53zy5pp4bbrxbruosuhuay6sm + '@azure-tools/cadl-ranch': 0.14.5_e53zy5pp4bbrxbruosuhuay6sm '@azure-tools/cadl-ranch-api': 0.4.6 - '@azure-tools/cadl-ranch-expect': 0.15.1_y3yxhtjg35d3g6m2dacugmsjfu + '@azure-tools/cadl-ranch-expect': 0.15.3_y3yxhtjg35d3g6m2dacugmsjfu '@azure-tools/typespec-azure-core': 0.45.0_alrznhyrw66bvx6gp4zrtdfkte '@typespec/compiler': 0.59.0 '@typespec/http': 0.59.0_@typespec+compiler@0.59.0 @@ -481,14 +481,14 @@ packages: - supports-color dev: true - /@azure-tools/cadl-ranch/0.14.3_e53zy5pp4bbrxbruosuhuay6sm: - resolution: {integrity: sha512-7UVhTi5dHR9hH6OnpWQONvDJGx5t5sU9q8BYHoJU0gdaTy0x23EHie5GTMg7aIFI896kuksu4iggUnQaCm2AnA==} + /@azure-tools/cadl-ranch/0.14.5_e53zy5pp4bbrxbruosuhuay6sm: + resolution: {integrity: sha512-vF98b9ru49YvzcFnuSW6A/gpDOSZcTd/0S42XnmTyTVuF+fp3XOatXTvoUlKnQ25du8hZTm7JFzcZeOova7Xbw==} engines: {node: '>=16.0.0'} hasBin: true dependencies: '@azure-tools/cadl-ranch-api': 0.4.6 - '@azure-tools/cadl-ranch-coverage-sdk': 0.8.3 - '@azure-tools/cadl-ranch-expect': 0.15.1_y3yxhtjg35d3g6m2dacugmsjfu + '@azure-tools/cadl-ranch-coverage-sdk': 0.8.4 + '@azure-tools/cadl-ranch-expect': 0.15.3_y3yxhtjg35d3g6m2dacugmsjfu '@azure/identity': 4.4.1 '@types/js-yaml': 4.0.6 '@typespec/compiler': 0.59.0 diff --git a/packages/typespec-ts/package.json b/packages/typespec-ts/package.json index 24714dd6fd..0e5e5e1e1c 100644 --- a/packages/typespec-ts/package.json +++ b/packages/typespec-ts/package.json @@ -63,9 +63,9 @@ "license": "MIT", "devDependencies": { "@azure-rest/core-client": "^2.1.0", - "@azure-tools/cadl-ranch-expect": "^0.15.1", - "@azure-tools/cadl-ranch-specs": "^0.36.1", - "@azure-tools/cadl-ranch": "^0.14.3", + "@azure-tools/cadl-ranch-expect": "^0.15.3", + "@azure-tools/cadl-ranch-specs": "^0.37.1", + "@azure-tools/cadl-ranch": "^0.14.5", "@azure-tools/cadl-ranch-api": "^0.4.6", "@azure-tools/typespec-autorest": ">=0.45.0 <1.0.0", "@azure-tools/typespec-azure-core": ">=0.45.0 <1.0.0", diff --git a/packages/typespec-ts/test/commands/cadl-ranch-list.js b/packages/typespec-ts/test/commands/cadl-ranch-list.js index 5ab34b92a2..913e0ab174 100644 --- a/packages/typespec-ts/test/commands/cadl-ranch-list.js +++ b/packages/typespec-ts/test/commands/cadl-ranch-list.js @@ -282,6 +282,10 @@ export const rlcTsps = [ { outputPath: "azure/client-generator-core/flatten-property", inputPath: "azure/client-generator-core/flatten-property" + }, + { + outputPath: "client/structure/client-operation-group", + inputPath: "client/structure/client-operation-group" } ]; @@ -546,6 +550,10 @@ export const modularTsps = [ { outputPath: "azure/core/page", inputPath: "azure/core/page" + }, + { + outputPath: "client/structure/client-operation-group", + inputPath: "client/structure/client-operation-group" } ]; diff --git a/packages/typespec-ts/test/integration/azureArmResources.spec.ts b/packages/typespec-ts/test/integration/azureArmResources.spec.ts index adc30390e1..3f1aea9ecd 100644 --- a/packages/typespec-ts/test/integration/azureArmResources.spec.ts +++ b/packages/typespec-ts/test/integration/azureArmResources.spec.ts @@ -1,6 +1,9 @@ import { assert } from "chai"; import AzureArmResourceClientFactory, { - AzureArmResourceClient + AzureArmResourceClient, + getLongRunningPoller, + isUnexpected, + paginate } from "./generated/azure/resource-manager/models/resources/src/index.js"; describe("Azure Arm Resources Rest Client", () => { let client: AzureArmResourceClient; @@ -49,8 +52,147 @@ describe("Azure Arm Resources Rest Client", () => { lastModifiedByType: "User" } }; - it("should get top level tracked resources", async () => { + + const validSingletonResource = { + id: `/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/resourceGroups/${RESOURCE_GROUP_EXPECTED}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources/default`, + name: "default", + type: "Azure.ResourceManager.Models.Resources/singletonTrackedResources", + location: "eastus", + properties: { + provisioningState: "Succeeded", + description: "valid" + }, + systemData: { + createdBy: "AzureSDK", + createdByType: "User", + createdAt: new Date(), + lastModifiedBy: "AzureSDK", + lastModifiedAt: new Date(), + lastModifiedByType: "User" + } + }; + // singleton tracked resource + it("should get singleton tracked resources by resourceGroup", async () => { + const initialResponse = await client + .path( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources/default", + "00000000-0000-0000-0000-000000000000", + "test-rg" + ) + .get(); + + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.status, "200"); + assert.strictEqual(result.body.id, validSingletonResource.id); + assert.strictEqual(result.body.name, validSingletonResource.name); + assert.strictEqual(result.body.type, validSingletonResource.type); + }); + + it("should createOrUpdate singleton tracked resources", async () => { + const initialResponse = await client + .path( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources/default", + "00000000-0000-0000-0000-000000000000", + "test-rg" + ) + .put({ + body: { + location: "eastus", + properties: { + description: "valid" + } + } + }); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validSingletonResource.id); + assert.strictEqual(result.body.name, validSingletonResource.name); + assert.strictEqual(result.body.type, validSingletonResource.type); + }); + + it("should update singleton tracked resources", async () => { + const initialResponse = await client + .path( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources/default", + "00000000-0000-0000-0000-000000000000", + "test-rg" + ) + .patch({ + body: { + location: "eastus2", + properties: { + description: "valid2" + } + } + }); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validSingletonResource.id); + assert.strictEqual(result.body.name, validSingletonResource.name); + assert.strictEqual(result.body.type, validSingletonResource.type); + assert.strictEqual(result.body.location, "eastus2"); + assert.strictEqual(result.body.properties?.description, "valid2"); + }); + + it("should list singleton tracked resources by resourceGroup", async () => { + const initialResponse = await client + .path( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources", + "00000000-0000-0000-0000-000000000000", + "test-rg" + ) + .get(); + if (isUnexpected(initialResponse)) { + const error = `Unexpected status code ${initialResponse.status}`; + assert.fail(error); + } + const iter = paginate(client, initialResponse); + let result = []; + for await (const item of iter) { + result.push(item); + } + assert.strictEqual(initialResponse.status, "200"); + assert.strictEqual(result[0]?.id, validSingletonResource.id); + assert.strictEqual(result[0]?.name, validSingletonResource.name); + assert.strictEqual(result[0]?.type, validSingletonResource.type); + }); + + // top level tracked resource + it("should actionSync top level tracked resources", async () => { const result = await client + .path( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/actionSync", + "00000000-0000-0000-0000-000000000000", + "test-rg", + "top" + ) + .post({ + body: { + message: "Resource action at top level.", + urgent: true + } + }); + assert.strictEqual(result.status, "204"); + }); + + it("should get top level tracked resources", async () => { + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}", "00000000-0000-0000-0000-000000000000", @@ -58,14 +200,19 @@ describe("Azure Arm Resources Rest Client", () => { "top" ) .get(); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.id, validTopLevelResource.id); - assert.strictEqual(body.name, validTopLevelResource.name); - assert.strictEqual(body.type, validTopLevelResource.type); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validTopLevelResource.id); + assert.strictEqual(result.body.name, validTopLevelResource.name); + assert.strictEqual(result.body.type, validTopLevelResource.type); }); it("should create or replace top level tracked resources", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}", "00000000-0000-0000-0000-000000000000", @@ -81,19 +228,24 @@ describe("Azure Arm Resources Rest Client", () => { } }); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.id, validTopLevelResource.id); - assert.strictEqual(body.name, validTopLevelResource.name); - assert.strictEqual(body.type, validTopLevelResource.type); - assert.strictEqual(body.location, validTopLevelResource.location); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validTopLevelResource.id); + assert.strictEqual(result.body.name, validTopLevelResource.name); + assert.strictEqual(result.body.type, validTopLevelResource.type); + assert.strictEqual(result.body.location, validTopLevelResource.location); assert.strictEqual( - body.properties?.description, + result.body.properties?.description, validTopLevelResource.properties?.description ); }); it.skip("should update top level tracked resources", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}", "00000000-0000-0000-0000-000000000000", @@ -108,13 +260,16 @@ describe("Azure Arm Resources Rest Client", () => { } } }); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.id, validTopLevelResource.id); - assert.strictEqual(body.name, validTopLevelResource.name); - assert.strictEqual(body.type, validTopLevelResource.type); - assert.strictEqual(body.location, validTopLevelResource.location); - assert.strictEqual(body.properties?.description, "valid2"); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validTopLevelResource.id); + assert.strictEqual(result.body.name, validTopLevelResource.name); + assert.strictEqual(result.body.type, validTopLevelResource.type); + assert.strictEqual(result.body.location, validTopLevelResource.location); + assert.strictEqual(result.body.properties?.description, "valid2"); }); it("should delete top level tracked resources", async () => { const result = await client @@ -134,38 +289,54 @@ describe("Azure Arm Resources Rest Client", () => { assert.strictEqual(result.status, "204"); }); it("should list top level tracked resources by resourceGroup ", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources", "00000000-0000-0000-0000-000000000000", "test-rg" ) .get(); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.value[0].id, validTopLevelResource.id); - assert.strictEqual(body.value[0].name, validTopLevelResource.name); - assert.strictEqual(body.value[0].type, validTopLevelResource.type); - assert.strictEqual(body.value[0].location, validTopLevelResource.location); + if (isUnexpected(initialResponse)) { + const error = `Unexpected status code ${initialResponse.status}`; + assert.fail(error); + } + const iter = paginate(client, initialResponse); + let result = []; + for await (const item of iter) { + result.push(item); + } + assert.strictEqual(initialResponse.status, "200"); + assert.strictEqual(result[0]?.id, validTopLevelResource.id); + assert.strictEqual(result[0]?.name, validTopLevelResource.name); + assert.strictEqual(result[0]?.type, validTopLevelResource.type); + assert.strictEqual(result[0]?.location, validTopLevelResource.location); }); it("should list top level tracked resources by subscription ", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources", "00000000-0000-0000-0000-000000000000" ) .get(); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.value[0].id, validTopLevelResource.id); - assert.strictEqual(body.value[0].name, validTopLevelResource.name); - assert.strictEqual(body.value[0].type, validTopLevelResource.type); - assert.strictEqual(body.value[0].location, validTopLevelResource.location); + if (isUnexpected(initialResponse)) { + const error = `Unexpected status code ${initialResponse.status}`; + assert.fail(error); + } + const iter = paginate(client, initialResponse); + let result = []; + for await (const item of iter) { + result.push(item); + } + assert.strictEqual(initialResponse.status, "200"); + assert.strictEqual(result[0]?.id, validTopLevelResource.id); + assert.strictEqual(result[0]?.name, validTopLevelResource.name); + assert.strictEqual(result[0]?.type, validTopLevelResource.type); + assert.strictEqual(result[0]?.location, validTopLevelResource.location); }); // nested proxy resource it("should get nested proxy resource", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/nestedProxyResources/{nextedProxyResourceName}", "00000000-0000-0000-0000-000000000000", @@ -174,15 +345,20 @@ describe("Azure Arm Resources Rest Client", () => { "nested" ) .get(); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.id, validNestedResource.id); - assert.strictEqual(body.name, validNestedResource.name); - assert.strictEqual(body.type, validNestedResource.type); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validNestedResource.id); + assert.strictEqual(result.body.name, validNestedResource.name); + assert.strictEqual(result.body.type, validNestedResource.type); }); it("should create or replace nested proxy resource", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/nestedProxyResources/{nextedProxyResourceName}", "00000000-0000-0000-0000-000000000000", @@ -197,19 +373,24 @@ describe("Azure Arm Resources Rest Client", () => { } } }); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.id, validNestedResource.id); - assert.strictEqual(body.name, validNestedResource.name); - assert.strictEqual(body.type, validNestedResource.type); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validNestedResource.id); + assert.strictEqual(result.body.name, validNestedResource.name); + assert.strictEqual(result.body.type, validNestedResource.type); assert.strictEqual( - body.properties?.description, + result.body.properties?.description, validNestedResource.properties?.description ); }); it("should update nested proxy resource", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/nestedProxyResources/{nextedProxyResourceName}", "00000000-0000-0000-0000-000000000000", @@ -224,12 +405,17 @@ describe("Azure Arm Resources Rest Client", () => { } } }); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.id, validNestedResource.id); - assert.strictEqual(body.name, validNestedResource.name); - assert.strictEqual(body.type, validNestedResource.type); - assert.strictEqual(body.properties?.description, "valid2"); + const poller = await getLongRunningPoller(client, initialResponse); + const result = await poller.pollUntilDone(); + assert.equal(result.status, "200"); + assert.strictEqual(initialResponse.status, "200"); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.body.id, validNestedResource.id); + assert.strictEqual(result.body.name, validNestedResource.name); + assert.strictEqual(result.body.type, validNestedResource.type); + assert.strictEqual(result.body.properties?.description, "valid2"); }); it("should delete nested proxy resource", async () => { @@ -246,7 +432,7 @@ describe("Azure Arm Resources Rest Client", () => { }); it("should list nested proxy resource by TopLevelTrackedResource ", async () => { - const result = await client + const initialResponse = await client .path( "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/nestedProxyResources", "00000000-0000-0000-0000-000000000000", @@ -254,10 +440,18 @@ describe("Azure Arm Resources Rest Client", () => { "top" ) .get(); - const body = JSON.parse(JSON.stringify(result.body)); - assert.strictEqual(result.status, "200"); - assert.strictEqual(body.value[0].id, validNestedResource.id); - assert.strictEqual(body.value[0].name, validNestedResource.name); - assert.strictEqual(body.value[0].type, validNestedResource.type); + if (isUnexpected(initialResponse)) { + const error = `Unexpected status code ${initialResponse.status}`; + assert.fail(error); + } + const iter = paginate(client, initialResponse); + let result = []; + for await (const item of iter) { + result.push(item); + } + assert.strictEqual(initialResponse.status, "200"); + assert.strictEqual(result[0]?.id, validNestedResource.id); + assert.strictEqual(result[0]?.name, validNestedResource.name); + assert.strictEqual(result[0]?.type, validNestedResource.type); }); }); diff --git a/packages/typespec-ts/test/integration/azurecore.spec.ts b/packages/typespec-ts/test/integration/azurecore.spec.ts index e6151e21a3..65a6e0e0e8 100644 --- a/packages/typespec-ts/test/integration/azurecore.spec.ts +++ b/packages/typespec-ts/test/integration/azurecore.spec.ts @@ -20,6 +20,12 @@ describe("Azure Core Rest Client", () => { name: "Madge", etag: "11bdc430-65e8-45ad-81d9-8ffa60d55b59" }; + const validUser2 = { + id: 2, + name: "John", + etag: "22bdc430-65e8-45ad-81d9-8ffa60d55b59" + }; + const expectBody = { users: [validUser, validUser2] }; it("should put user", async () => { try { const result = await client.path("/azure/core/basic/users/{id}", 1).put({ @@ -142,4 +148,23 @@ describe("Azure Core Rest Client", () => { assert.fail(err as string); } }); + + it("should export all users", async () => { + try { + const result = await client + .path("/azure/core/basic/users:exportallusers") + .post({ + queryParameters: { + format: "json" + } + }); + if (isUnexpected(result)) { + throw Error("Unexpected status code"); + } + assert.strictEqual(result.status, "200"); + assert.deepEqual(result.body, expectBody); + } catch (err) { + assert.fail(err as string); + } + }); }); diff --git a/packages/typespec-ts/test/integration/clientStructure.spec.ts b/packages/typespec-ts/test/integration/clientStructure.spec.ts index 7a3d5e5abd..f4ec69fdd9 100644 --- a/packages/typespec-ts/test/integration/clientStructure.spec.ts +++ b/packages/typespec-ts/test/integration/clientStructure.spec.ts @@ -11,12 +11,16 @@ import ServiceRenamedClientFactory, { import ServiceTwoOpGroupClientFactory, { ServiceClient as ServiceTwoOpGroupClient } from "./generated/client/structure/two-operation-group/src/index.js"; +import ServiceOperationGroupClientFactory, { + ServiceClient as ServiceOperationGroupClient +} from "./generated/client/structure/client-operation-group/src/index.js"; import { assert } from "chai"; describe("ClientStructureClient Rest Client", () => { let client0: ServiceDefaultClient; let client1: ServiceMultiClient; let client2: ServiceRenamedClient; let client3: ServiceTwoOpGroupClient; + let client4: ServiceOperationGroupClient; let clientArray: Client[]; beforeEach(() => { @@ -44,7 +48,14 @@ describe("ClientStructureClient Rest Client", () => { allowInsecureConnection: true } ); - clientArray = new Array(...[client0, client1, client2, client3]); + client4 = ServiceOperationGroupClientFactory( + "http://localhost:3000", + "default", + { + allowInsecureConnection: true + } + ); + clientArray = [client0, client1, client2, client3, client4]; }); it("should call operation one correctly", async () => { diff --git a/packages/typespec-ts/test/integration/generated/azure/core/basic/src/index.d.ts b/packages/typespec-ts/test/integration/generated/azure/core/basic/src/index.d.ts index a2fc75cd51..de8a8124f1 100644 --- a/packages/typespec-ts/test/integration/generated/azure/core/basic/src/index.d.ts +++ b/packages/typespec-ts/test/integration/generated/azure/core/basic/src/index.d.ts @@ -110,6 +110,35 @@ export declare interface Export200Response extends HttpResponse { body: UserOutput; } +export declare interface ExportAllUsers { + post(options: ExportAllUsersParameters): StreamableMethod; +} + +export declare interface ExportAllUsers200Response extends HttpResponse { + status: "200"; + body: UserListOutput; +} + +export declare interface ExportAllUsersDefaultHeaders { + "x-ms-error-code"?: string; +} + +export declare interface ExportAllUsersDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponse; + headers: RawHttpHeaders & ExportAllUsersDefaultHeaders; +} + +export declare type ExportAllUsersParameters = ExportAllUsersQueryParam & RequestParameters; + +export declare interface ExportAllUsersQueryParam { + queryParameters: ExportAllUsersQueryParamProperties; +} + +export declare interface ExportAllUsersQueryParamProperties { + format: string; +} + export declare interface ExportDefaultHeaders { "x-ms-error-code"?: string; } @@ -166,6 +195,8 @@ export declare function isUnexpected(response: List200Response | ListDefaultResp export declare function isUnexpected(response: Export200Response | ExportDefaultResponse): response is ExportDefaultResponse; +export declare function isUnexpected(response: ExportAllUsers200Response | ExportAllUsersDefaultResponse): response is ExportAllUsersDefaultResponse; + export declare interface List { get(options?: ListParameters): StreamableMethod; } @@ -219,6 +250,7 @@ export declare interface Routes { (path: "/azure/core/basic/users/{id}", id: number): CreateOrUpdate; (path: "/azure/core/basic/users"): List; (path: "/azure/core/basic/users/{id}:export", id: number): Export; + (path: "/azure/core/basic/users:exportallusers"): ExportAllUsers; } export declare interface User { @@ -226,6 +258,10 @@ export declare interface User { orders?: Array; } +export declare interface UserListOutput { + users: Array; +} + export declare interface UserOrder { userId: number; detail: string; diff --git a/packages/typespec-ts/test/integration/generated/azure/resource-manager/models/resources/src/index.d.ts b/packages/typespec-ts/test/integration/generated/azure/resource-manager/models/resources/src/index.d.ts index 2d37ef2dea..2412eaf834 100644 --- a/packages/typespec-ts/test/integration/generated/azure/resource-manager/models/resources/src/index.d.ts +++ b/packages/typespec-ts/test/integration/generated/azure/resource-manager/models/resources/src/index.d.ts @@ -71,6 +71,8 @@ export declare function getLongRunningPoller(client: Client, initialResponse: NestedProxyResourcesDelete202Response | NestedProxyResourcesDelete204Response | NestedProxyResourcesDeleteDefaultResponse, options?: CreateHttpPollerOptions>): Promise, TResult>>; +export declare function getLongRunningPoller(client: Client, initialResponse: SingletonTrackedResourcesCreateOrUpdate200Response | SingletonTrackedResourcesCreateOrUpdate201Response | SingletonTrackedResourcesCreateOrUpdateDefaultResponse, options?: CreateHttpPollerOptions>): Promise, TResult>>; + export declare type GetPage = (pageLink: string, maxPageSize?: number) => Promise<{ page: TPage; nextPageLink?: string; @@ -110,6 +112,14 @@ export declare function isUnexpected(response: NestedProxyResourcesDelete202Resp export declare function isUnexpected(response: NestedProxyResourcesListByTopLevelTrackedResource200Response | NestedProxyResourcesListByTopLevelTrackedResourceDefaultResponse): response is NestedProxyResourcesListByTopLevelTrackedResourceDefaultResponse; +export declare function isUnexpected(response: SingletonTrackedResourcesGetByResourceGroup200Response | SingletonTrackedResourcesGetByResourceGroupDefaultResponse): response is SingletonTrackedResourcesGetByResourceGroupDefaultResponse; + +export declare function isUnexpected(response: SingletonTrackedResourcesCreateOrUpdate200Response | SingletonTrackedResourcesCreateOrUpdate201Response | SingletonTrackedResourcesCreateOrUpdateLogicalResponse | SingletonTrackedResourcesCreateOrUpdateDefaultResponse): response is SingletonTrackedResourcesCreateOrUpdateDefaultResponse; + +export declare function isUnexpected(response: SingletonTrackedResourcesUpdate200Response | SingletonTrackedResourcesUpdateDefaultResponse): response is SingletonTrackedResourcesUpdateDefaultResponse; + +export declare function isUnexpected(response: SingletonTrackedResourcesListByResourceGroup200Response | SingletonTrackedResourcesListByResourceGroupDefaultResponse): response is SingletonTrackedResourcesListByResourceGroupDefaultResponse; + export declare interface NestedProxyResource extends ProxyResource { properties?: NestedProxyResourceProperties; } @@ -403,6 +413,8 @@ export declare interface Routes { (path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/actionSync", subscriptionId: string, resourceGroupName: string, topLevelTrackedResourceName: string): TopLevelTrackedResourcesActionSync; (path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/nestedProxyResources/{nextedProxyResourceName}", subscriptionId: string, resourceGroupName: string, topLevelTrackedResourceName: string, nextedProxyResourceName: string): NestedProxyResourcesGet; (path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/topLevelTrackedResources/{topLevelTrackedResourceName}/nestedProxyResources", subscriptionId: string, resourceGroupName: string, topLevelTrackedResourceName: string): NestedProxyResourcesListByTopLevelTrackedResource; + (path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources/default", subscriptionId: string, resourceGroupName: string): SingletonTrackedResourcesGetByResourceGroup; + (path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources", subscriptionId: string, resourceGroupName: string): SingletonTrackedResourcesListByResourceGroup; } export declare interface SimplePollerLike, TResult> { @@ -423,6 +435,107 @@ export declare interface SimplePollerLike isStopped(): boolean; } +export declare interface SingletonTrackedResource extends TrackedResource { + properties?: SingletonTrackedResourceProperties; +} + +export declare type SingletonTrackedResourceListResultOutput = Paged; + +export declare interface SingletonTrackedResourceOutput extends TrackedResourceOutput { + properties?: SingletonTrackedResourcePropertiesOutput; +} + +export declare interface SingletonTrackedResourceProperties { + description?: string; +} + +export declare interface SingletonTrackedResourcePropertiesOutput { + readonly provisioningState?: ProvisioningStateOutput; + description?: string; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdate200Response extends HttpResponse { + status: "200"; + body: SingletonTrackedResourceOutput; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdate201Headers { + "azure-asyncoperation"?: string; + "retry-after"?: number; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdate201Response extends HttpResponse { + status: "201"; + body: SingletonTrackedResourceOutput; + headers: RawHttpHeaders & SingletonTrackedResourcesCreateOrUpdate201Headers; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdateBodyParam { + body: SingletonTrackedResource; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdateDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponseOutput; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdateLogicalResponse extends HttpResponse { + status: "200"; + body: SingletonTrackedResourceOutput; +} + +export declare type SingletonTrackedResourcesCreateOrUpdateParameters = SingletonTrackedResourcesCreateOrUpdateBodyParam & RequestParameters; + +export declare interface SingletonTrackedResourcesGetByResourceGroup { + get(options?: SingletonTrackedResourcesGetByResourceGroupParameters): StreamableMethod; + put(options: SingletonTrackedResourcesCreateOrUpdateParameters): StreamableMethod; + patch(options: SingletonTrackedResourcesUpdateParameters): StreamableMethod; +} + +export declare interface SingletonTrackedResourcesGetByResourceGroup200Response extends HttpResponse { + status: "200"; + body: SingletonTrackedResourceOutput; +} + +export declare interface SingletonTrackedResourcesGetByResourceGroupDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponseOutput; +} + +export declare type SingletonTrackedResourcesGetByResourceGroupParameters = RequestParameters; + +export declare interface SingletonTrackedResourcesListByResourceGroup { + get(options?: SingletonTrackedResourcesListByResourceGroupParameters): StreamableMethod; +} + +export declare interface SingletonTrackedResourcesListByResourceGroup200Response extends HttpResponse { + status: "200"; + body: SingletonTrackedResourceListResultOutput; +} + +export declare interface SingletonTrackedResourcesListByResourceGroupDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponseOutput; +} + +export declare type SingletonTrackedResourcesListByResourceGroupParameters = RequestParameters; + +export declare interface SingletonTrackedResourcesUpdate200Response extends HttpResponse { + status: "200"; + body: SingletonTrackedResourceOutput; +} + +export declare interface SingletonTrackedResourcesUpdateBodyParam { + body: SingletonTrackedResource; +} + +export declare interface SingletonTrackedResourcesUpdateDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponseOutput; +} + +export declare type SingletonTrackedResourcesUpdateParameters = SingletonTrackedResourcesUpdateBodyParam & RequestParameters; + export declare interface Sku { name: string; tier?: SkuTier; diff --git a/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/.gitignore b/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/.gitignore new file mode 100644 index 0000000000..39220655cc --- /dev/null +++ b/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/.gitignore @@ -0,0 +1,6 @@ +/** +!/src +/src/** +!/src/index.d.ts +!/.gitignore +!/tspconfig.yaml diff --git a/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/src/index.d.ts b/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/src/index.d.ts new file mode 100644 index 0000000000..0bf0726fdb --- /dev/null +++ b/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/src/index.d.ts @@ -0,0 +1,121 @@ +import { Client } from '@azure-rest/core-client'; +import { ClientOptions } from '@azure-rest/core-client'; +import { HttpResponse } from '@azure-rest/core-client'; +import { RequestParameters } from '@azure-rest/core-client'; +import { StreamableMethod } from '@azure-rest/core-client'; + +export declare type ClientType = "default" | "multi-client" | "renamed-operation" | "two-operation-group"; + +declare function createClient(endpointParam: string, clientParam: ClientType, options?: ServiceClientOptions): ServiceClient; +export default createClient; + +export declare interface Eight { + post(options?: EightParameters): StreamableMethod; +} + +export declare interface Eight204Response extends HttpResponse { + status: "204"; +} + +export declare type EightParameters = RequestParameters; + +export declare interface Five { + post(options?: FiveParameters): StreamableMethod; +} + +export declare interface Five204Response extends HttpResponse { + status: "204"; +} + +export declare type FiveParameters = RequestParameters; + +export declare interface Four { + post(options?: FourParameters): StreamableMethod; +} + +export declare interface Four204Response extends HttpResponse { + status: "204"; +} + +export declare type FourParameters = RequestParameters; + +export declare interface Nine { + post(options?: NineParameters): StreamableMethod; +} + +export declare interface Nine204Response extends HttpResponse { + status: "204"; +} + +export declare type NineParameters = RequestParameters; + +export declare interface One { + post(options?: OneParameters): StreamableMethod; +} + +export declare interface One204Response extends HttpResponse { + status: "204"; +} + +export declare type OneParameters = RequestParameters; + +export declare interface Routes { + (path: "/one"): One; + (path: "/two"): Two; + (path: "/seven"): Seven; + (path: "/eight"): Eight; + (path: "/nine"): Nine; + (path: "/three"): Three; + (path: "/four"): Four; + (path: "/five"): Five; + (path: "/six"): Six; +} + +export declare type ServiceClient = Client & { + path: Routes; +}; + +export declare interface ServiceClientOptions extends ClientOptions { +} + +export declare interface Seven { + post(options?: SevenParameters): StreamableMethod; +} + +export declare interface Seven204Response extends HttpResponse { + status: "204"; +} + +export declare type SevenParameters = RequestParameters; + +export declare interface Six { + post(options?: SixParameters): StreamableMethod; +} + +export declare interface Six204Response extends HttpResponse { + status: "204"; +} + +export declare type SixParameters = RequestParameters; + +export declare interface Three { + post(options?: ThreeParameters): StreamableMethod; +} + +export declare interface Three204Response extends HttpResponse { + status: "204"; +} + +export declare type ThreeParameters = RequestParameters; + +export declare interface Two { + post(options?: TwoParameters): StreamableMethod; +} + +export declare interface Two204Response extends HttpResponse { + status: "204"; +} + +export declare type TwoParameters = RequestParameters; + +export { } diff --git a/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/tspconfig.yaml b/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/tspconfig.yaml new file mode 100644 index 0000000000..2f30e37fb3 --- /dev/null +++ b/packages/typespec-ts/test/integration/generated/client/structure/client-operation-group/tspconfig.yaml @@ -0,0 +1,14 @@ +emit: + - "@azure-tools/typespec-ts" +options: + "@azure-tools/typespec-ts": + "emitter-output-dir": "{project-root}" + generateMetadata: true + generateTest: false + addCredentials: false + azureSdkForJs: false + isTypeSpecTest: true + packageDetails: + name: "@msinternal/client-operation-group" + description: "Client Structure operationGroupOperaiton Test Service" + version: "1.0.0" diff --git a/packages/typespec-ts/test/integration/generated/payload/multipart/src/index.d.ts b/packages/typespec-ts/test/integration/generated/payload/multipart/src/index.d.ts index 3cf86ae810..c2f795aae7 100644 --- a/packages/typespec-ts/test/integration/generated/payload/multipart/src/index.d.ts +++ b/packages/typespec-ts/test/integration/generated/payload/multipart/src/index.d.ts @@ -8,18 +8,6 @@ export declare interface Address { city: string; } -export declare interface AnonymousModel { - post(options: FormDataAnonymousModelParameters): StreamableMethod; -} - -export declare interface Basic { - post(options: FormDataBasicParameters): StreamableMethod; -} - -export declare interface BinaryArrayParts { - post(options: FormDataBinaryArrayPartsParameters): StreamableMethod; -} - export declare type BinaryArrayPartsRequest = FormData | Array; export declare interface BinaryArrayPartsRequestIdPartDescriptor { @@ -34,14 +22,6 @@ export declare interface BinaryArrayPartsRequestPicturesPartDescriptor { contentType?: string; } -export declare interface CheckFileNameAndContentType { - post(options: FormDataCheckFileNameAndContentTypeParameters): StreamableMethod; -} - -export declare interface Complex { - post(options: FormDataComplexParameters): StreamableMethod; -} - export declare type ComplexHttpPartsModelRequest = FormData | Array; export declare interface ComplexHttpPartsModelRequestAddressPartDescriptor { @@ -95,17 +75,9 @@ export declare interface ComplexPartsRequestProfileImagePartDescriptor { contentType?: string; } -export declare interface ComplexWithHttpPart { - post(options: FormDataComplexWithHttpPartParameters): StreamableMethod; -} - declare function createClient(options?: MultiPartClientOptions): MultiPartClient; export default createClient; -export declare interface FileWithHttpPartOptionalContentType { - post(options: FormDataFileWithHttpPartOptionalContentTypeParameters): StreamableMethod; -} - export declare type FileWithHttpPartOptionalContentTypeRequest = FormData | Array; export declare interface FileWithHttpPartOptionalContentTypeRequestProfileImagePartDescriptor { @@ -113,10 +85,6 @@ export declare interface FileWithHttpPartOptionalContentTypeRequestProfileImageP body: HttpPart; } -export declare interface FileWithHttpPartRequiredContentType { - post(options: FormDataFileWithHttpPartRequiredContentTypeParameters): StreamableMethod; -} - export declare type FileWithHttpPartRequiredContentTypeRequest = FormData | Array; export declare interface FileWithHttpPartRequiredContentTypeRequestProfileImagePartDescriptor { @@ -124,10 +92,6 @@ export declare interface FileWithHttpPartRequiredContentTypeRequestProfileImageP body: HttpPart; } -export declare interface FileWithHttpPartSpecificContentType { - post(options: FormDataFileWithHttpPartSpecificContentTypeParameters): StreamableMethod; -} - export declare type FileWithHttpPartSpecificContentTypeRequest = FormData | Array; export declare interface FileWithHttpPartSpecificContentTypeRequestProfileImagePartDescriptor { @@ -135,6 +99,10 @@ export declare interface FileWithHttpPartSpecificContentTypeRequestProfileImageP body: HttpPart; } +export declare interface FormDataAnonymousModel { + post(options: FormDataAnonymousModelParameters): StreamableMethod; +} + export declare interface FormDataAnonymousModel204Response extends HttpResponse { status: "204"; } @@ -154,6 +122,10 @@ export declare interface FormDataAnonymousModelMediaTypesParam { export declare type FormDataAnonymousModelParameters = FormDataAnonymousModelMediaTypesParam & FormDataAnonymousModelBodyParam & RequestParameters; +export declare interface FormDataBasic { + post(options: FormDataBasicParameters): StreamableMethod; +} + export declare interface FormDataBasic204Response extends HttpResponse { status: "204"; } @@ -168,6 +140,10 @@ export declare interface FormDataBasicMediaTypesParam { export declare type FormDataBasicParameters = FormDataBasicMediaTypesParam & FormDataBasicBodyParam & RequestParameters; +export declare interface FormDataBinaryArrayParts { + post(options: FormDataBinaryArrayPartsParameters): StreamableMethod; +} + export declare interface FormDataBinaryArrayParts204Response extends HttpResponse { status: "204"; } @@ -182,6 +158,10 @@ export declare interface FormDataBinaryArrayPartsMediaTypesParam { export declare type FormDataBinaryArrayPartsParameters = FormDataBinaryArrayPartsMediaTypesParam & FormDataBinaryArrayPartsBodyParam & RequestParameters; +export declare interface FormDataCheckFileNameAndContentType { + post(options: FormDataCheckFileNameAndContentTypeParameters): StreamableMethod; +} + export declare interface FormDataCheckFileNameAndContentType204Response extends HttpResponse { status: "204"; } @@ -196,75 +176,120 @@ export declare interface FormDataCheckFileNameAndContentTypeMediaTypesParam { export declare type FormDataCheckFileNameAndContentTypeParameters = FormDataCheckFileNameAndContentTypeMediaTypesParam & FormDataCheckFileNameAndContentTypeBodyParam & RequestParameters; -export declare interface FormDataComplex204Response extends HttpResponse { +export declare interface FormDataFileArrayAndBasic { + post(options: FormDataFileArrayAndBasicParameters): StreamableMethod; +} + +export declare interface FormDataFileArrayAndBasic204Response extends HttpResponse { status: "204"; } -export declare interface FormDataComplexBodyParam { +export declare interface FormDataFileArrayAndBasicBodyParam { body: ComplexPartsRequest; } -export declare interface FormDataComplexMediaTypesParam { +export declare interface FormDataFileArrayAndBasicMediaTypesParam { contentType: "multipart/form-data"; } -export declare type FormDataComplexParameters = FormDataComplexMediaTypesParam & FormDataComplexBodyParam & RequestParameters; +export declare type FormDataFileArrayAndBasicParameters = FormDataFileArrayAndBasicMediaTypesParam & FormDataFileArrayAndBasicBodyParam & RequestParameters; -export declare interface FormDataComplexWithHttpPart204Response extends HttpResponse { +export declare interface FormDataHttpPartsContentTypeImageJpegContentType { + post(options: FormDataHttpPartsContentTypeImageJpegContentTypeParameters): StreamableMethod; +} + +export declare interface FormDataHttpPartsContentTypeImageJpegContentType204Response extends HttpResponse { status: "204"; } -export declare interface FormDataComplexWithHttpPartBodyParam { - body: ComplexHttpPartsModelRequest; +export declare interface FormDataHttpPartsContentTypeImageJpegContentTypeBodyParam { + body: FileWithHttpPartSpecificContentTypeRequest; } -export declare interface FormDataComplexWithHttpPartMediaTypesParam { +export declare interface FormDataHttpPartsContentTypeImageJpegContentTypeMediaTypesParam { contentType: "multipart/form-data"; } -export declare type FormDataComplexWithHttpPartParameters = FormDataComplexWithHttpPartMediaTypesParam & FormDataComplexWithHttpPartBodyParam & RequestParameters; +export declare type FormDataHttpPartsContentTypeImageJpegContentTypeParameters = FormDataHttpPartsContentTypeImageJpegContentTypeMediaTypesParam & FormDataHttpPartsContentTypeImageJpegContentTypeBodyParam & RequestParameters; + +export declare interface FormDataHttpPartsContentTypeOptionalContentType { + post(options: FormDataHttpPartsContentTypeOptionalContentTypeParameters): StreamableMethod; +} -export declare interface FormDataFileWithHttpPartOptionalContentType204Response extends HttpResponse { +export declare interface FormDataHttpPartsContentTypeOptionalContentType204Response extends HttpResponse { status: "204"; } -export declare interface FormDataFileWithHttpPartOptionalContentTypeBodyParam { +export declare interface FormDataHttpPartsContentTypeOptionalContentTypeBodyParam { body: FileWithHttpPartOptionalContentTypeRequest; } -export declare interface FormDataFileWithHttpPartOptionalContentTypeMediaTypesParam { +export declare interface FormDataHttpPartsContentTypeOptionalContentTypeMediaTypesParam { contentType: "multipart/form-data"; } -export declare type FormDataFileWithHttpPartOptionalContentTypeParameters = FormDataFileWithHttpPartOptionalContentTypeMediaTypesParam & FormDataFileWithHttpPartOptionalContentTypeBodyParam & RequestParameters; +export declare type FormDataHttpPartsContentTypeOptionalContentTypeParameters = FormDataHttpPartsContentTypeOptionalContentTypeMediaTypesParam & FormDataHttpPartsContentTypeOptionalContentTypeBodyParam & RequestParameters; -export declare interface FormDataFileWithHttpPartRequiredContentType204Response extends HttpResponse { +export declare interface FormDataHttpPartsContentTypeRequiredContentType { + post(options: FormDataHttpPartsContentTypeRequiredContentTypeParameters): StreamableMethod; +} + +export declare interface FormDataHttpPartsContentTypeRequiredContentType204Response extends HttpResponse { status: "204"; } -export declare interface FormDataFileWithHttpPartRequiredContentTypeBodyParam { +export declare interface FormDataHttpPartsContentTypeRequiredContentTypeBodyParam { body: FileWithHttpPartRequiredContentTypeRequest; } -export declare interface FormDataFileWithHttpPartRequiredContentTypeMediaTypesParam { +export declare interface FormDataHttpPartsContentTypeRequiredContentTypeMediaTypesParam { contentType: "multipart/form-data"; } -export declare type FormDataFileWithHttpPartRequiredContentTypeParameters = FormDataFileWithHttpPartRequiredContentTypeMediaTypesParam & FormDataFileWithHttpPartRequiredContentTypeBodyParam & RequestParameters; +export declare type FormDataHttpPartsContentTypeRequiredContentTypeParameters = FormDataHttpPartsContentTypeRequiredContentTypeMediaTypesParam & FormDataHttpPartsContentTypeRequiredContentTypeBodyParam & RequestParameters; + +export declare interface FormDataHttpPartsJsonArrayAndFileArray { + post(options: FormDataHttpPartsJsonArrayAndFileArrayParameters): StreamableMethod; +} -export declare interface FormDataFileWithHttpPartSpecificContentType204Response extends HttpResponse { +export declare interface FormDataHttpPartsJsonArrayAndFileArray204Response extends HttpResponse { status: "204"; } -export declare interface FormDataFileWithHttpPartSpecificContentTypeBodyParam { - body: FileWithHttpPartSpecificContentTypeRequest; +export declare interface FormDataHttpPartsJsonArrayAndFileArrayBodyParam { + body: ComplexHttpPartsModelRequest; +} + +export declare interface FormDataHttpPartsJsonArrayAndFileArrayMediaTypesParam { + contentType: "multipart/form-data"; +} + +export declare type FormDataHttpPartsJsonArrayAndFileArrayParameters = FormDataHttpPartsJsonArrayAndFileArrayMediaTypesParam & FormDataHttpPartsJsonArrayAndFileArrayBodyParam & RequestParameters; + +export declare interface FormDataHttpPartsNonStringFloat { + post(options: FormDataHttpPartsNonStringFloatParameters): StreamableMethod; +} + +export declare interface FormDataHttpPartsNonStringFloat204Response extends HttpResponse { + status: "204"; } -export declare interface FormDataFileWithHttpPartSpecificContentTypeMediaTypesParam { +export declare interface FormDataHttpPartsNonStringFloatBodyParam { + body: FormData | Array<{ + name: "temperature"; + body: HttpPart; + }>; +} + +export declare interface FormDataHttpPartsNonStringFloatMediaTypesParam { contentType: "multipart/form-data"; } -export declare type FormDataFileWithHttpPartSpecificContentTypeParameters = FormDataFileWithHttpPartSpecificContentTypeMediaTypesParam & FormDataFileWithHttpPartSpecificContentTypeBodyParam & RequestParameters; +export declare type FormDataHttpPartsNonStringFloatParameters = FormDataHttpPartsNonStringFloatMediaTypesParam & FormDataHttpPartsNonStringFloatBodyParam & RequestParameters; + +export declare interface FormDataJsonPart { + post(options: FormDataJsonPartParameters): StreamableMethod; +} export declare interface FormDataJsonPart204Response extends HttpResponse { status: "204"; @@ -280,6 +305,10 @@ export declare interface FormDataJsonPartMediaTypesParam { export declare type FormDataJsonPartParameters = FormDataJsonPartMediaTypesParam & FormDataJsonPartBodyParam & RequestParameters; +export declare interface FormDataMultiBinaryParts { + post(options: FormDataMultiBinaryPartsParameters): StreamableMethod; +} + export declare interface FormDataMultiBinaryParts204Response extends HttpResponse { status: "204"; } @@ -297,10 +326,6 @@ export declare type FormDataMultiBinaryPartsParameters = FormDataMultiBinaryPart export declare interface HttpPart { } -export declare interface JsonPart { - post(options: FormDataJsonPartParameters): StreamableMethod; -} - export declare type JsonPartRequest = FormData | Array; export declare interface JsonPartRequestAddressPartDescriptor { @@ -315,10 +340,6 @@ export declare interface JsonPartRequestProfileImagePartDescriptor { contentType?: string; } -export declare interface MultiBinaryParts { - post(options: FormDataMultiBinaryPartsParameters): StreamableMethod; -} - export declare type MultiBinaryPartsRequest = FormData | Array; export declare interface MultiBinaryPartsRequestPicturePartDescriptor { @@ -357,17 +378,18 @@ export declare interface MultiPartRequestProfileImagePartDescriptor { } export declare interface Routes { - (path: "/multipart/form-data/mixed-parts"): Basic; - (path: "/multipart/form-data/complex-parts"): Complex; - (path: "/multipart/form-data/json-part"): JsonPart; - (path: "/multipart/form-data/binary-array-parts"): BinaryArrayParts; - (path: "/multipart/form-data/multi-binary-parts"): MultiBinaryParts; - (path: "/multipart/form-data/check-filename-and-content-type"): CheckFileNameAndContentType; - (path: "/multipart/form-data/anonymous-model"): AnonymousModel; - (path: "/multipart/form-data/check-filename-and-specific-content-type-with-httppart"): FileWithHttpPartSpecificContentType; - (path: "/multipart/form-data/check-filename-and-required-content-type-with-httppart"): FileWithHttpPartRequiredContentType; - (path: "/multipart/form-data/file-with-http-part-optional-content-type"): FileWithHttpPartOptionalContentType; - (path: "/multipart/form-data/complex-parts-with-httppart"): ComplexWithHttpPart; + (path: "/multipart/form-data/mixed-parts"): FormDataBasic; + (path: "/multipart/form-data/complex-parts"): FormDataFileArrayAndBasic; + (path: "/multipart/form-data/json-part"): FormDataJsonPart; + (path: "/multipart/form-data/binary-array-parts"): FormDataBinaryArrayParts; + (path: "/multipart/form-data/multi-binary-parts"): FormDataMultiBinaryParts; + (path: "/multipart/form-data/check-filename-and-content-type"): FormDataCheckFileNameAndContentType; + (path: "/multipart/form-data/anonymous-model"): FormDataAnonymousModel; + (path: "/multipart/form-data/complex-parts-with-httppart"): FormDataHttpPartsJsonArrayAndFileArray; + (path: "/multipart/form-data/check-filename-and-specific-content-type-with-httppart"): FormDataHttpPartsContentTypeImageJpegContentType; + (path: "/multipart/form-data/check-filename-and-required-content-type-with-httppart"): FormDataHttpPartsContentTypeRequiredContentType; + (path: "/multipart/form-data/file-with-http-part-optional-content-type"): FormDataHttpPartsContentTypeOptionalContentType; + (path: "/multipart/form-data/non-string-float"): FormDataHttpPartsNonStringFloat; } export { } diff --git a/packages/typespec-ts/test/modularIntegration/azureArmResources.spec.ts b/packages/typespec-ts/test/modularIntegration/azureArmResources.spec.ts index b27b8ab12f..28f96f9565 100644 --- a/packages/typespec-ts/test/modularIntegration/azureArmResources.spec.ts +++ b/packages/typespec-ts/test/modularIntegration/azureArmResources.spec.ts @@ -47,6 +47,91 @@ describe("Azure Arm Resources Rest Client", () => { lastModifiedByType: "User" } }; + + const validSingletonResource = { + id: `/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/resourceGroups/${RESOURCE_GROUP_EXPECTED}/providers/Azure.ResourceManager.Models.Resources/singletonTrackedResources/default`, + name: "default", + type: "Azure.ResourceManager.Models.Resources/singletonTrackedResources", + location: "eastus", + properties: { + provisioningState: "Succeeded", + description: "valid" + }, + systemData: { + createdBy: "AzureSDK", + createdByType: "User", + createdAt: new Date(), + lastModifiedBy: "AzureSDK", + lastModifiedAt: new Date(), + lastModifiedByType: "User" + } + }; + + // singleton tracked resource + it("should get singleton tracked resources by resourceGroup", async () => { + const result = + await client.singletonTrackedResources.getByResourceGroup("test-rg"); + assert.strictEqual(result.id, validSingletonResource.id); + assert.strictEqual(result.name, validSingletonResource.name); + assert.strictEqual(result.type, validSingletonResource.type); + }); + + it("should update singleton tracked resources", async () => { + const result = await client.singletonTrackedResources.update("test-rg", { + location: "eastus2", + properties: { + description: "valid2" + } + }); + + assert.strictEqual(result.id, validSingletonResource.id); + assert.strictEqual(result.name, validSingletonResource.name); + assert.strictEqual(result.type, validSingletonResource.type); + assert.strictEqual(result.location, "eastus2"); + assert.strictEqual(result.properties?.description, "valid2"); + }); + + it("should createOrUpdate singleton tracked resources by resourceGroup", async () => { + const result = await client.singletonTrackedResources.createOrUpdate( + "test-rg", + { + location: "eastus", + properties: { + description: "valid" + } + } + ); + assert.strictEqual(result.id, validSingletonResource.id); + assert.strictEqual(result.name, validSingletonResource.name); + assert.strictEqual(result.type, validSingletonResource.type); + }); + + it("should list singleton tracked resources by resourceGroup", async () => { + const result = + client.singletonTrackedResources.listByResourceGroup("test-rg"); + const items = []; + for await (const user of result) { + items.push(user); + } + console.log(items); + assert.strictEqual(items[0]?.id, validSingletonResource.id); + assert.strictEqual(items[0]?.name, validSingletonResource.name); + assert.strictEqual(items[0]?.type, validSingletonResource.type); + }); + + // top level tracked resource + it("should actionSync top level tracked resources", async () => { + const result = await client.topLevelTrackedResources.actionSync( + "test-rg", + "top", + { + message: "Resource action at top level.", + urgent: true + } + ); + assert.isUndefined(result); + }); + it("should get top level tracked resources", async () => { const result = await client.topLevelTrackedResources.get("test-rg", "top"); assert.strictEqual(result.id, validTopLevelResource.id); diff --git a/packages/typespec-ts/test/modularIntegration/azureCore.spec.ts b/packages/typespec-ts/test/modularIntegration/azureCore.spec.ts index 791285943e..af33a2a059 100644 --- a/packages/typespec-ts/test/modularIntegration/azureCore.spec.ts +++ b/packages/typespec-ts/test/modularIntegration/azureCore.spec.ts @@ -112,7 +112,25 @@ describe("BasicClient Classical Client", () => { assert.fail(err as string); } }); - + it("should export all users", async () => { + try { + const result = await client.exportAllUsers("json"); + assert.strictEqual(result.users[0]?.id, 1); + assert.strictEqual(result.users[0]?.name, "Madge"); + assert.strictEqual( + result.users[0]?.etag, + "11bdc430-65e8-45ad-81d9-8ffa60d55b59" + ); + assert.strictEqual(result.users[1]?.id, 2); + assert.strictEqual(result.users[1]?.name, "John"); + assert.strictEqual( + result.users[1]?.etag, + "22bdc430-65e8-45ad-81d9-8ffa60d55b59" + ); + } catch (err) { + assert.fail(err as string); + } + }); it("should create or replace a user", async () => { try { const user = await client.createOrReplace( diff --git a/packages/typespec-ts/test/modularIntegration/clientStructureOperationGroup.spec.ts b/packages/typespec-ts/test/modularIntegration/clientStructureOperationGroup.spec.ts new file mode 100644 index 0000000000..d2ce93fe46 --- /dev/null +++ b/packages/typespec-ts/test/modularIntegration/clientStructureOperationGroup.spec.ts @@ -0,0 +1,48 @@ +import { + FirstClient, + SecondClient +} from "./generated/client/structure/client-operation-group/src/index.js"; +import { assert } from "chai"; +describe("Client Structure Operation Group Rest Client", () => { + let client1: FirstClient; + let client2: SecondClient; + + beforeEach(() => { + client1 = new FirstClient("http://localhost:3002", "default", { + allowInsecureConnection: true + }); + client2 = new SecondClient("http://localhost:3002", "default", { + allowInsecureConnection: true + }); + }); + + it("should call operation one correctly", async () => { + const result = await client1.one(); + assert.strictEqual(result, undefined); + }); + + it("should call operation two correctly", async () => { + const result = await client1.group3.two(); + assert.strictEqual(result, undefined); + }); + + it("should call operation three correctly", async () => { + const result = await client1.group3.three(); + assert.strictEqual(result, undefined); + }); + + it("should call operation four correctly", async () => { + const result = await client1.group4.four(); + assert.strictEqual(result, undefined); + }); + + it("should call operation five correctly", async () => { + const result = await client2.five(); + assert.strictEqual(result, undefined); + }); + + it("should call operation six correctly", async () => { + const result = await client2.group5.six(); + assert.strictEqual(result, undefined); + }); +}); diff --git a/packages/typespec-ts/test/modularIntegration/generated/azure/core/basic/src/index.d.ts b/packages/typespec-ts/test/modularIntegration/generated/azure/core/basic/src/index.d.ts index d1376a712d..39837a0c13 100644 --- a/packages/typespec-ts/test/modularIntegration/generated/azure/core/basic/src/index.d.ts +++ b/packages/typespec-ts/test/modularIntegration/generated/azure/core/basic/src/index.d.ts @@ -12,6 +12,7 @@ export declare class BasicClient { list(options?: ListOptionalParams): PagedAsyncIterableIterator; delete(id: number, options?: DeleteOptionalParams): Promise; export(id: number, format: string, options?: ExportOptionalParams): Promise; + exportAllUsers(format: string, options?: ExportAllUsersOptionalParams): Promise; } export declare interface BasicClientOptionalParams extends ClientOptions { @@ -32,6 +33,9 @@ export declare interface CreateOrUpdateOptionalParams extends OperationOptions { export declare interface DeleteOptionalParams extends OperationOptions { } +export declare interface ExportAllUsersOptionalParams extends OperationOptions { +} + export declare interface ExportOptionalParams extends OperationOptions { } @@ -65,6 +69,10 @@ export declare interface User { readonly etag: string; } +export declare interface UserList { + users: User[]; +} + export declare interface UserOrder { readonly id: number; userId: number; diff --git a/packages/typespec-ts/test/modularIntegration/generated/azure/resource-manager/models/resources/src/index.d.ts b/packages/typespec-ts/test/modularIntegration/generated/azure/resource-manager/models/resources/src/index.d.ts index d795ff9f3f..d8a4481667 100644 --- a/packages/typespec-ts/test/modularIntegration/generated/azure/resource-manager/models/resources/src/index.d.ts +++ b/packages/typespec-ts/test/modularIntegration/generated/azure/resource-manager/models/resources/src/index.d.ts @@ -112,6 +112,7 @@ export declare class ResourcesClient { constructor(subscriptionId: string, options?: ResourcesClientOptionalParams); readonly topLevelTrackedResources: TopLevelTrackedResourcesOperations; readonly nestedProxyResources: NestedProxyResourcesOperations; + readonly singletonTrackedResources: SingletonTrackedResourcesOperations; } export declare interface ResourcesClientOptionalParams extends ClientOptions { @@ -126,6 +127,35 @@ export declare interface RestorePollerOptions Promise; } +export declare interface SingletonTrackedResource extends TrackedResource { + properties?: SingletonTrackedResourceProperties; +} + +export declare interface SingletonTrackedResourceProperties { + readonly provisioningState?: ProvisioningState; + description?: string; +} + +export declare interface SingletonTrackedResourcesCreateOrUpdateOptionalParams extends OperationOptions { + updateIntervalInMs?: number; +} + +export declare interface SingletonTrackedResourcesGetByResourceGroupOptionalParams extends OperationOptions { +} + +export declare interface SingletonTrackedResourcesListByResourceGroupOptionalParams extends OperationOptions { +} + +export declare interface SingletonTrackedResourcesOperations { + getByResourceGroup: (resourceGroupName: string, options?: SingletonTrackedResourcesGetByResourceGroupOptionalParams) => Promise; + createOrUpdate: (resourceGroupName: string, resource: SingletonTrackedResource, options?: SingletonTrackedResourcesCreateOrUpdateOptionalParams) => PollerLike, SingletonTrackedResource>; + update: (resourceGroupName: string, properties: SingletonTrackedResource, options?: SingletonTrackedResourcesUpdateOptionalParams) => Promise; + listByResourceGroup: (resourceGroupName: string, options?: SingletonTrackedResourcesListByResourceGroupOptionalParams) => PagedAsyncIterableIterator; +} + +export declare interface SingletonTrackedResourcesUpdateOptionalParams extends OperationOptions { +} + export declare interface SystemData { createdBy?: string; createdByType?: CreatedByType; diff --git a/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/.gitignore b/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/.gitignore new file mode 100644 index 0000000000..39220655cc --- /dev/null +++ b/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/.gitignore @@ -0,0 +1,6 @@ +/** +!/src +/src/** +!/src/index.d.ts +!/.gitignore +!/tspconfig.yaml diff --git a/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/src/index.d.ts b/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/src/index.d.ts new file mode 100644 index 0000000000..8a900ce95d --- /dev/null +++ b/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/src/index.d.ts @@ -0,0 +1,63 @@ +import { ClientOptions } from '@azure-rest/core-client'; +import { OperationOptions } from '@azure-rest/core-client'; +import { Pipeline } from '@azure/core-rest-pipeline'; + +export declare type ClientType = "default" | "multi-client" | "renamed-operation" | "two-operation-group"; + +export declare class FirstClient { + private _client; + readonly pipeline: Pipeline; + constructor(endpointParam: string, clientParam: ClientType, options?: FirstClientOptionalParams); + one(options?: OneOptionalParams): Promise; + readonly group3: Group3Operations; + readonly group4: Group4Operations; +} + +export declare interface FirstClientOptionalParams extends ClientOptions { +} + +export declare interface FiveOptionalParams extends OperationOptions { +} + +export declare interface Group3Operations { + two: (options?: Group3TwoOptionalParams) => Promise; + three: (options?: Group3ThreeOptionalParams) => Promise; +} + +export declare interface Group3ThreeOptionalParams extends OperationOptions { +} + +export declare interface Group3TwoOptionalParams extends OperationOptions { +} + +export declare interface Group4FourOptionalParams extends OperationOptions { +} + +export declare interface Group4Operations { + four: (options?: Group4FourOptionalParams) => Promise; +} + +export declare interface Group5Operations { + six: (options?: Group5SixOptionalParams) => Promise; +} + +export declare interface Group5SixOptionalParams extends OperationOptions { +} + +export declare interface OneOptionalParams extends OperationOptions { +} + +export declare class SecondClient { + private _client; + readonly pipeline: Pipeline; + constructor(endpointParam: string, clientParam: SecondClientClientType, options?: SecondClientOptionalParams); + five(options?: FiveOptionalParams): Promise; + readonly group5: Group5Operations; +} + +export declare type SecondClientClientType = "default" | "multi-client" | "renamed-operation" | "two-operation-group"; + +export declare interface SecondClientOptionalParams extends ClientOptions { +} + +export { } diff --git a/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/tspconfig.yaml b/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/tspconfig.yaml new file mode 100644 index 0000000000..c03daf8d2b --- /dev/null +++ b/packages/typespec-ts/test/modularIntegration/generated/client/structure/client-operation-group/tspconfig.yaml @@ -0,0 +1,14 @@ +emit: + - "@azure-tools/typespec-ts" +options: + "@azure-tools/typespec-ts": + "emitter-output-dir": "{project-root}" + generateTest: false + addCredentials: false + azureSdkForJs: false + isTypeSpecTest: true + isModularLibrary: true + packageDetails: + name: "@msinternal/client-operation-group" + description: "Client Structure OperaitonGroup Test Service" + version: "1.0.0"