diff --git a/common/changes/@cadl-lang/compiler/fix-proj-plus-GET_2022-08-06-01-58.json b/common/changes/@cadl-lang/compiler/fix-proj-plus-GET_2022-08-06-01-58.json new file mode 100644 index 0000000000..28dad57fd4 --- /dev/null +++ b/common/changes/@cadl-lang/compiler/fix-proj-plus-GET_2022-08-06-01-58.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cadl-lang/compiler", + "comment": "Run projections on types returned from getEffectiveType", + "type": "patch" + } + ], + "packageName": "@cadl-lang/compiler" +} \ No newline at end of file diff --git a/common/changes/@cadl-lang/openapi3/fix-proj-plus-GET_2022-08-06-01-58.json b/common/changes/@cadl-lang/openapi3/fix-proj-plus-GET_2022-08-06-01-58.json new file mode 100644 index 0000000000..e2ee484c99 --- /dev/null +++ b/common/changes/@cadl-lang/openapi3/fix-proj-plus-GET_2022-08-06-01-58.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cadl-lang/openapi3", + "comment": "Run projections on types returned from getEffectiveType", + "type": "patch" + } + ], + "packageName": "@cadl-lang/openapi3" +} \ No newline at end of file diff --git a/packages/compiler/core/checker.ts b/packages/compiler/core/checker.ts index 0cc0f67100..c4fc004c8a 100644 --- a/packages/compiler/core/checker.ts +++ b/packages/compiler/core/checker.ts @@ -4110,6 +4110,19 @@ export function createChecker(program: Program): Checker { return false; } + function getProjectedEffectiveModelType(type: ModelType): ModelType { + if (!program.currentProjector) { + return type; + } + + const projectedType = program.currentProjector.projectType(type); + if (projectedType.kind !== "Model") { + compilerAssert(false, "Fail"); + } + + return projectedType; + } + function getEffectiveModelType( model: ModelType, filter?: (property: ModelTypeProperty) => boolean @@ -4120,7 +4133,7 @@ export function createChecker(program: Program): Checker { if (model.name) { // named model - return model; + return getProjectedEffectiveModelType(model); } // We would need to change the algorithm if this doesn't hold. We @@ -4188,7 +4201,7 @@ export function createChecker(program: Program): Checker { } } - return match ?? model; + return match ? getProjectedEffectiveModelType(match) : model; } function filterModelProperties( diff --git a/packages/compiler/lib/decorators.ts b/packages/compiler/lib/decorators.ts index a87419fadf..a6f1b96234 100644 --- a/packages/compiler/lib/decorators.ts +++ b/packages/compiler/lib/decorators.ts @@ -449,7 +449,7 @@ export function $withOptionalProperties(context: DecoratorContext, target: Type) target.properties.forEach((p) => (p.optional = true)); } -// -- @withUpdatableProperties decorator ---------------------- +// -- @withUpdateableProperties decorator ---------------------- export function $withUpdateableProperties(context: DecoratorContext, target: Type) { if (!validateDecoratorTarget(context, target, "@withUpdateableProperties", "Model")) { diff --git a/packages/openapi3/test/test-host.ts b/packages/openapi3/test/test-host.ts index 591e8735cb..ef89b111f3 100644 --- a/packages/openapi3/test/test-host.ts +++ b/packages/openapi3/test/test-host.ts @@ -15,14 +15,22 @@ export async function createOpenAPITestHost() { }); } -export async function createOpenAPITestRunner() { +export async function createOpenAPITestRunner({ + withVersioning, +}: { withVersioning?: boolean } = {}) { const host = await createOpenAPITestHost(); - return createTestWrapper( - host, - (code) => - `import "@cadl-lang/rest"; import "@cadl-lang/openapi"; import "@cadl-lang/openapi3"; using Cadl.Rest; using Cadl.Http; using OpenAPI; ${code}`, - { emitters: { "@cadl-lang/openapi3": {} } } - ); + const importAndUsings = ` + import "@cadl-lang/rest"; import "@cadl-lang/openapi"; + import "@cadl-lang/openapi3"; + ${withVersioning ? `import "@cadl-lang/versioning"` : ""}; + using Cadl.Rest; + using Cadl.Http; + using OpenAPI; + ${withVersioning ? "using Cadl.Versioning;" : ""} +`; + return createTestWrapper(host, (code) => `${importAndUsings} ${code}`, { + emitters: { "@cadl-lang/openapi3": {} }, + }); } function versionedOutput(path: string, version: string) { diff --git a/packages/openapi3/test/versioning.test.ts b/packages/openapi3/test/versioning.test.ts index 725a701012..f5567038a7 100644 --- a/packages/openapi3/test/versioning.test.ts +++ b/packages/openapi3/test/versioning.test.ts @@ -1,7 +1,7 @@ import { DecoratorContext, NamespaceType } from "@cadl-lang/compiler"; import { createTestWrapper } from "@cadl-lang/compiler/testing"; import { deepStrictEqual, strictEqual } from "assert"; -import { createOpenAPITestHost, openApiFor } from "./test-host.js"; +import { createOpenAPITestHost, createOpenAPITestRunner, openApiFor } from "./test-host.js"; describe("openapi3: versioning", () => { it("works with models", async () => { @@ -118,19 +118,16 @@ describe("openapi3: versioning", () => { `import "@cadl-lang/rest"; import "@cadl-lang/openapi"; import "@cadl-lang/openapi3"; import "@cadl-lang/versioning"; import "./test.js"; - using Cadl.Rest; using Cadl.Http; using OpenAPI; using Cadl.Versioning; ${code}`, { emitters: { "@cadl-lang/openapi3": {} } } ); await runner.compile(` - @versioned(Contoso.Library.Versions) namespace Contoso.Library { namespace Blah { } enum Versions { v1 }; } - @armNamespace @serviceTitle("Widgets 'r' Us") @versionedDependency(Contoso.Library.Versions.v1) @@ -140,7 +137,6 @@ describe("openapi3: versioning", () => { @segment("widgets") id: string; } - interface Operations { @test op get(id: string): Widget; @@ -150,4 +146,33 @@ describe("openapi3: versioning", () => { strictEqual(storedNamespace, "Contoso.WidgetService"); }); + + // Test for https://github.com/microsoft/cadl/issues/812 + it("doesn't throw errors when using UpdateableProperties", async () => { + // if this test throws a duplicate name diagnostic, check that getEffectiveType + // is returning the projected type. + const runner = await createOpenAPITestRunner({ withVersioning: true }); + await runner.compile(` + @versioned(Library.Versions) + namespace Library { + enum Versions { + v1, + v2, + } + } + + @serviceTitle("Service") + @versionedDependency(Library.Versions.v1) + namespace Service { + model Widget { + details?: WidgetDetails; + } + + model WidgetDetails {} + interface Projects { + oops(...UpdateableProperties): Widget; + } + } + `); + }); });