From faf128633b446091a596e46ae85399cb323c8608 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 07:38:06 -0800 Subject: [PATCH 1/4] Bump core from `9bf31a6` to `83a0c3f` (#180) Bumps [core](https://github.com/microsoft/typespec) from `9bf31a6` to `83a0c3f`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 9bf31a65c8..83a0c3f92e 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 9bf31a65c8cb0b05a6ffdb0693c3f07c1313485b +Subproject commit 83a0c3f92e1ff4fea1d86afed36d7804795198b6 From 78246545faa50feb5d6ff21f5bd23b7c35c523c6 Mon Sep 17 00:00:00 2001 From: Allen Zhang Date: Mon, 29 Jan 2024 11:12:14 -0800 Subject: [PATCH 2/4] Update example file values to avoid model validation errors (#177) --- .../examples/2021-10-01-preview/Employees_CreateOrUpdate.json | 4 ++-- eng/feeds/arm/examples/2021-10-01-preview/Employees_Get.json | 2 +- .../2021-10-01-preview/Employees_ListByResourceGroup.json | 2 +- .../2021-10-01-preview/Employees_ListBySubscription.json | 2 +- .../arm/examples/2021-10-01-preview/Employees_Update.json | 2 +- .../arm/examples/2021-10-01-preview/Operations_List.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/eng/feeds/arm/examples/2021-10-01-preview/Employees_CreateOrUpdate.json b/eng/feeds/arm/examples/2021-10-01-preview/Employees_CreateOrUpdate.json index 0488455763..9b34209dcd 100644 --- a/eng/feeds/arm/examples/2021-10-01-preview/Employees_CreateOrUpdate.json +++ b/eng/feeds/arm/examples/2021-10-01-preview/Employees_CreateOrUpdate.json @@ -31,7 +31,7 @@ "key2913": "urperxmkkhhkp" }, "location": "itajgxyqozseoygnl", - "id": "dnkyotqlrefuwxribpzbl", + "id": "/subscriptions/11809CA1-E126-4017-945E-AA795CD5C5A9/resourceGroups/rgopenapi/providers/Microsoft.Contoso/employees/le-8MU--J3W6q8D386p3-iT3", "name": "xepyxhpb", "type": "svvamxrdnnv", "systemData": { @@ -59,7 +59,7 @@ "key2913": "urperxmkkhhkp" }, "location": "itajgxyqozseoygnl", - "id": "dnkyotqlrefuwxribpzbl", + "id": "/subscriptions/11809CA1-E126-4017-945E-AA795CD5C5A9/resourceGroups/rgopenapi/providers/Microsoft.Contoso/employees/9KF-f-8b", "name": "xepyxhpb", "type": "svvamxrdnnv", "systemData": { diff --git a/eng/feeds/arm/examples/2021-10-01-preview/Employees_Get.json b/eng/feeds/arm/examples/2021-10-01-preview/Employees_Get.json index 9dddb10ec0..3ee7ff5b9c 100644 --- a/eng/feeds/arm/examples/2021-10-01-preview/Employees_Get.json +++ b/eng/feeds/arm/examples/2021-10-01-preview/Employees_Get.json @@ -20,7 +20,7 @@ "key2913": "urperxmkkhhkp" }, "location": "itajgxyqozseoygnl", - "id": "dnkyotqlrefuwxribpzbl", + "id": "/subscriptions/11809CA1-E126-4017-945E-AA795CD5C5A9/resourceGroups/rgopenapi/providers/Microsoft.Contoso/employees/le-8MU--J3W6q8D386p3-iT3", "name": "xepyxhpb", "type": "svvamxrdnnv", "systemData": { diff --git a/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListByResourceGroup.json b/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListByResourceGroup.json index d69d5da321..f6512d2ba7 100644 --- a/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListByResourceGroup.json +++ b/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListByResourceGroup.json @@ -21,7 +21,7 @@ "key2913": "urperxmkkhhkp" }, "location": "itajgxyqozseoygnl", - "id": "dnkyotqlrefuwxribpzbl", + "id": "/subscriptions/11809CA1-E126-4017-945E-AA795CD5C5A9/resourceGroups/rgopenapi/providers/Microsoft.Contoso/employees/test", "name": "xepyxhpb", "type": "svvamxrdnnv", "systemData": { diff --git a/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListBySubscription.json b/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListBySubscription.json index a56830812f..ffa095dd1d 100644 --- a/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListBySubscription.json +++ b/eng/feeds/arm/examples/2021-10-01-preview/Employees_ListBySubscription.json @@ -20,7 +20,7 @@ "key2913": "urperxmkkhhkp" }, "location": "itajgxyqozseoygnl", - "id": "dnkyotqlrefuwxribpzbl", + "id": "/subscriptions/11809CA1-E126-4017-945E-AA795CD5C5A9/resourceGroups/rgopenapi/providers/Microsoft.Contoso/employees/test", "name": "xepyxhpb", "type": "svvamxrdnnv", "systemData": { diff --git a/eng/feeds/arm/examples/2021-10-01-preview/Employees_Update.json b/eng/feeds/arm/examples/2021-10-01-preview/Employees_Update.json index c4ada32ff0..f3f85a4656 100644 --- a/eng/feeds/arm/examples/2021-10-01-preview/Employees_Update.json +++ b/eng/feeds/arm/examples/2021-10-01-preview/Employees_Update.json @@ -30,7 +30,7 @@ "key2913": "urperxmkkhhkp" }, "location": "itajgxyqozseoygnl", - "id": "dnkyotqlrefuwxribpzbl", + "id": "/subscriptions/11809CA1-E126-4017-945E-AA795CD5C5A9/resourceGroups/contoso/providers/Microsoft.Contoso/employees/test", "name": "xepyxhpb", "type": "svvamxrdnnv", "systemData": { diff --git a/eng/feeds/arm/examples/2021-10-01-preview/Operations_List.json b/eng/feeds/arm/examples/2021-10-01-preview/Operations_List.json index 29986520ea..6185e205aa 100644 --- a/eng/feeds/arm/examples/2021-10-01-preview/Operations_List.json +++ b/eng/feeds/arm/examples/2021-10-01-preview/Operations_List.json @@ -21,7 +21,7 @@ "actionType": "Internal" } ], - "nextLink": "bamebrbqkebjwevbq" + "nextLink": "https://sample.com/nextLink" } } } From 3f98132a317d967aa495f9d66072583b58a9c5f0 Mon Sep 17 00:00:00 2001 From: iscai-msft <43154838+iscai-msft@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:50:18 -0500 Subject: [PATCH 3/4] create multipart file sdktype for bytes in a multipart formdata model (#166) --- .changeset/chilly-foxes-type.md | 5 ++ .../src/interfaces.ts | 12 ++- .../typespec-client-generator-core/src/lib.ts | 7 ++ .../src/types.ts | 34 ++++++++ .../test/types.test.ts | 78 +++++++++++++++++++ 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 .changeset/chilly-foxes-type.md diff --git a/.changeset/chilly-foxes-type.md b/.changeset/chilly-foxes-type.md new file mode 100644 index 0000000000..fabe435e55 --- /dev/null +++ b/.changeset/chilly-foxes-type.md @@ -0,0 +1,5 @@ +--- +"@azure-tools/typespec-client-generator-core": patch +--- + +add MultipartFile type diff --git a/packages/typespec-client-generator-core/src/interfaces.ts b/packages/typespec-client-generator-core/src/interfaces.ts index 3d3500da04..cfef5d2c2e 100644 --- a/packages/typespec-client-generator-core/src/interfaces.ts +++ b/packages/typespec-client-generator-core/src/interfaces.ts @@ -83,7 +83,8 @@ export type SdkType = | SdkEnumValueType | SdkConstantType | SdkUnionType - | SdkModelType; + | SdkModelType + | SdkMultipartFileType; export interface SdkBuiltInType extends SdkTypeBase { kind: SdkBuiltInKinds; @@ -110,7 +111,8 @@ export type SdkBuiltInKinds = | "armId" | "ipAddress" | "azureLocation" - | "etag"; + | "etag" + | "multipartFile"; const SdkDatetimeEncodingsConst = ["rfc3339", "rfc7231", "unixTimestamp"] as const; @@ -130,6 +132,11 @@ export interface SdkDurationType extends SdkTypeBase { wireType: SdkBuiltInType; } +export interface SdkMultipartFileType extends SdkTypeBase { + kind: "multipartFile"; + encode: "binary"; +} + export interface SdkArrayType extends SdkTypeBase { kind: "array"; valueType: SdkType; @@ -188,6 +195,7 @@ export interface SdkModelType extends SdkTypeBase { kind: "model"; properties: SdkModelPropertyType[]; name: string; + isFormDataType: boolean; generatedName?: string; description?: string; details?: string; diff --git a/packages/typespec-client-generator-core/src/lib.ts b/packages/typespec-client-generator-core/src/lib.ts index fb88c7d740..181c9c4d4a 100644 --- a/packages/typespec-client-generator-core/src/lib.ts +++ b/packages/typespec-client-generator-core/src/lib.ts @@ -67,6 +67,13 @@ export const $lib = createTypeSpecLibrary({ wrongType: paramMessage`Encoding '${"encoding"}' cannot be used on type '${"type"}'`, }, }, + "conflicting-multipart-model-usage": { + severity: "error", + messages: { + default: "Invalid encoding", + wrongType: paramMessage`Model '${"modelName"}' cannot be used as both multipart/form-data input and regular body input. You can create a separate model with name 'model ${"modelName"}FormData' extends ${"modelName"} {}`, + }, + }, "discriminator-not-constant": { severity: "error", messages: { diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index dc245eca8e..8329b9fbcd 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -66,6 +66,7 @@ import { SdkEnumValueType, SdkModelPropertyTypeBase, SdkModelType, + SdkMultipartFileType, SdkTupleType, SdkType, } from "./interfaces.js"; @@ -266,6 +267,13 @@ export function getSdkDurationType(context: SdkContext, type: Scalar): SdkDurati }; } +function getSdkMultipartFileType(context: SdkContext, type: Scalar): SdkMultipartFileType { + return { + ...getSdkTypeBaseHelper(context, type, "multipartFile"), + encode: "binary", + }; +} + export function getSdkArrayOrDict( context: SdkContext, type: Model, @@ -433,8 +441,24 @@ function addDiscriminatorToModelType( export function getSdkModel(context: SdkContext, type: Model, operation?: Operation): SdkModelType { type = getEffectivePayloadType(context, type); let sdkType = context.modelsMap?.get(type) as SdkModelType | undefined; + const httpOperation = operation + ? ignoreDiagnostics(getHttpOperation(context.program, operation)) + : undefined; + const isFormDataType = httpOperation + ? Boolean(httpOperation.parameters.body?.contentTypes.includes("multipart/form-data")) + : false; if (sdkType) { updateModelsMap(context, type, sdkType, operation); + if (isFormDataType !== sdkType.isFormDataType) { + // This means we have a model that is used both for formdata input and for regular body input + reportDiagnostic(context.program, { + code: "conflicting-multipart-model-usage", + target: type, + format: { + modelName: sdkType.name, + }, + }); + } } else { const docWrapper = getDocHelper(context, type); sdkType = { @@ -448,6 +472,7 @@ export function getSdkModel(context: SdkContext, type: Model, operation?: Operat access: undefined, // dummy value since we need to update models map before we can set this usage: UsageFlags.None, // dummy value since we need to update models map before we can set this crossLanguageDefinitionId: getCrossLanguageDefinitionId(type), + isFormDataType, }; updateModelsMap(context, type, sdkType, operation); @@ -650,6 +675,15 @@ export function getClientType(context: SdkContext, type: Type, operation?: Opera if (type.name === "duration") { return getSdkDurationType(context, type); } + const httpOperation = operation + ? ignoreDiagnostics(getHttpOperation(context.program, operation)) + : undefined; + const hasMultipartInput = + httpOperation && + httpOperation.parameters.body?.contentTypes.includes("multipart/form-data"); + if (type.name === "bytes" && hasMultipartInput) { + return getSdkMultipartFileType(context, type); + } const scalarType = getSdkBuiltInType(context, type); // just add default encode, normally encode is on extended scalar and model property addEncodeInfo(context, type, scalarType); diff --git a/packages/typespec-client-generator-core/test/types.test.ts b/packages/typespec-client-generator-core/test/types.test.ts index dbd330f320..e049185f51 100644 --- a/packages/typespec-client-generator-core/test/types.test.ts +++ b/packages/typespec-client-generator-core/test/types.test.ts @@ -1,5 +1,6 @@ import { AzureCoreTestLibrary } from "@azure-tools/typespec-azure-core/testing"; import { Enum, UsageFlags } from "@typespec/compiler"; +import { expectDiagnostics } from "@typespec/compiler/testing"; import { deepEqual, deepStrictEqual, strictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; import { @@ -1983,6 +1984,83 @@ describe("typespec-client-generator-core: types", () => { strictEqual(models.length, 2); }); }); + describe("SdkMultipartFormType", () => { + it("multipart form basic", async function () { + await runner.compileWithBuiltInService(` + model MultiPartRequest { + id: string; + profileImage: bytes; + } + + op basic(@header contentType: "multipart/form-data", @body body: MultiPartRequest): NoContentResponse; + `); + + const models = Array.from(getAllModels(runner.context)); + strictEqual(models.length, 1); + const model = models[0] as SdkModelType; + strictEqual(model.kind, "model"); + strictEqual(model.isFormDataType, true); + strictEqual(model.name, "MultiPartRequest"); + strictEqual(model.properties.length, 2); + const id = model.properties.find((x) => x.nameInClient === "id")!; + strictEqual(id.kind, "property"); + strictEqual(id.type.kind, "string"); + const profileImage = model.properties.find((x) => x.nameInClient === "profileImage")!; + strictEqual(profileImage.kind, "property"); + strictEqual(profileImage.type.kind, "multipartFile"); + }); + it("multipart conflicting model usage", async function () { + const diagnostics = await runner.diagnose( + ` + @service({title: "Test Service"}) namespace TestService; + model MultiPartRequest { + id: string; + profileImage: bytes; + } + + @post op multipartUse(@header contentType: "multipart/form-data", @body body: MultiPartRequest): NoContentResponse; + @put op jsonUse(@body body: MultiPartRequest): NoContentResponse; + ` + ); + getAllModels(runner.context); + expectDiagnostics(diagnostics, { + code: "@azure-tools/typespec-client-generator-core/conflicting-multipart-model-usage", + }); + + // expectDiagnostics(getAllModels(runner.context), { + // code: "@azure-tools/typespec-client-generator-core/conflicting-multipart-model-usage", + // }); + }); + it("multipart resolving conflicting model usage with spread", async function () { + await runner.compileWithBuiltInService( + ` + model B { + doc: bytes + } + + model A { + ...B + } + + @put op multipartOperation(@header contentType: "multipart/form-data", ...A): void; + @post op normalOperation(...B): void; + ` + ); + const models = Array.from(getAllModels(runner.context)); + strictEqual(models.length, 2); + const modelA = models.find((x) => x.name === "A")!; + strictEqual(modelA.kind, "model"); + strictEqual(modelA.isFormDataType, true); + strictEqual(modelA.properties.length, 1); + strictEqual(modelA.properties[0].type.kind, "multipartFile"); + + const modelB = models.find((x) => x.name === "B")!; + strictEqual(modelB.kind, "model"); + strictEqual(modelB.isFormDataType, false); + strictEqual(modelB.properties.length, 1); + strictEqual(modelB.properties[0].type.kind, "bytes"); + }); + }); describe("SdkTupleType", () => { it("model with tupled properties", async function () { await runner.compileAndDiagnose(` From f699d8ef9d94f0202bbf1d3998460376dcbddfda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:45:20 +0000 Subject: [PATCH 4/4] Bump @azure-tools/cadl-ranch-specs from 0.28.7 to 0.29.0 in /packages/e2e-tests/cadl-ranch-specs (#181) Bumps [@azure-tools/cadl-ranch-specs](https://github.com/Azure/cadl-ranch) from 0.28.7 to 0.29.0.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@azure-tools/cadl-ranch-specs&package-manager=npm_and_yarn&previous-version=0.28.7&new-version=0.29.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/e2e-tests/cadl-ranch-specs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e-tests/cadl-ranch-specs/package.json b/packages/e2e-tests/cadl-ranch-specs/package.json index 26c9a30c81..7167aa5c18 100644 --- a/packages/e2e-tests/cadl-ranch-specs/package.json +++ b/packages/e2e-tests/cadl-ranch-specs/package.json @@ -1,7 +1,7 @@ { "name": "@azure-tools/typespec-e2e-cadl-ranch-specs", "dependencies": { - "@azure-tools/cadl-ranch-specs": "0.28.7" + "@azure-tools/cadl-ranch-specs": "0.29.0" }, "type": "module", "private": true