diff --git a/common/changes/@typespec/openapi3/versioning-IncompatibleDecoratorUsage_2023-04-27-16-38.json b/common/changes/@typespec/openapi3/versioning-IncompatibleDecoratorUsage_2023-04-27-16-38.json new file mode 100644 index 00000000000..d9a70151de5 --- /dev/null +++ b/common/changes/@typespec/openapi3/versioning-IncompatibleDecoratorUsage_2023-04-27-16-38.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/openapi3", + "comment": "", + "type": "none" + } + ], + "packageName": "@typespec/openapi3" +} \ No newline at end of file diff --git a/common/changes/@typespec/versioning/versioning-IncompatibleDecoratorUsage_2023-04-27-16-29.json b/common/changes/@typespec/versioning/versioning-IncompatibleDecoratorUsage_2023-04-27-16-29.json new file mode 100644 index 00000000000..4f9b443fafc --- /dev/null +++ b/common/changes/@typespec/versioning/versioning-IncompatibleDecoratorUsage_2023-04-27-16-29.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/versioning", + "comment": "Raise error if versioned spec specifies a single service version.", + "type": "none" + } + ], + "packageName": "@typespec/versioning" +} \ No newline at end of file diff --git a/packages/openapi3/test/versioning.test.ts b/packages/openapi3/test/versioning.test.ts index 336931d76b7..d05e7b3d6a7 100644 --- a/packages/openapi3/test/versioning.test.ts +++ b/packages/openapi3/test/versioning.test.ts @@ -8,7 +8,7 @@ describe("openapi3: versioning", () => { const { v1, v2, v3 } = await openApiFor( ` @versioned(Versions) - @service({title: "My Service", version: "hi"}) + @service({title: "My Service"}) namespace MyService { enum Versions { @useDependency(MyLibrary.Versions.A) diff --git a/packages/versioning/src/lib.ts b/packages/versioning/src/lib.ts index 09de0a43d12..e57a11bd4db 100644 --- a/packages/versioning/src/lib.ts +++ b/packages/versioning/src/lib.ts @@ -51,6 +51,12 @@ const libDef = { default: "@renamedFrom.oldName cannot be empty string.", }, }, + "no-service-fixed-version": { + severity: "error", + messages: { + default: paramMessage`Namespace '${"name"}' cannot specify a fixed service version with @service({version: ${"version"}}) while using @versioned. Remove the version argument from @service.`, + }, + }, "incompatible-versioned-reference": { severity: "error", messages: { diff --git a/packages/versioning/src/validate.ts b/packages/versioning/src/validate.ts index 1e7d6ad1da4..7fe46c70b16 100644 --- a/packages/versioning/src/validate.ts +++ b/packages/versioning/src/validate.ts @@ -1,5 +1,6 @@ import { getNamespaceFullName, + getService, getTypeName, isTemplateInstance, Namespace, @@ -93,6 +94,18 @@ export function $onValidate(program: Program) { } }, namespace: (namespace) => { + const [_, versionMap] = getVersions(program, namespace); + const serviceProps = getService(program, namespace); + if (serviceProps?.version !== undefined && versionMap !== undefined) { + reportDiagnostic(program, { + code: "no-service-fixed-version", + format: { + name: getNamespaceFullName(namespace), + version: serviceProps.version, + }, + target: namespace, + }); + } const versionedNamespace = findVersionedNamespace(program, namespace); const dependencies = getVersionDependencies(program, namespace); if (dependencies === undefined) { diff --git a/packages/versioning/test/incompatible-versioning.test.ts b/packages/versioning/test/incompatible-versioning.test.ts index 09adb03bb15..c01debc1225 100644 --- a/packages/versioning/test/incompatible-versioning.test.ts +++ b/packages/versioning/test/incompatible-versioning.test.ts @@ -7,6 +7,43 @@ import { } from "@typespec/compiler/testing"; import { createVersioningTestHost, createVersioningTestRunner } from "./test-host.js"; +describe("versioning: incompatible use of decorators", () => { + let runner: BasicTestRunner; + let host: TestHost; + const imports: string[] = []; + + beforeEach(async () => { + host = await createVersioningTestHost(); + runner = createTestWrapper(host, { + wrapper: (code) => ` + import "@typespec/versioning"; + ${imports.map((i) => `import "${i}";`).join("\n")} + using TypeSpec.Versioning; + ${code}`, + }); + }); + + it("emit diagnostic when @service({version: 'X'}) is used with @versioned", async () => { + const diagnostics = await runner.diagnose(` + @versioned(Versions) + @service({ + title: "Widget Service", + version: "v3" + }) + namespace DemoService; + + enum Versions { + v1, + v2, + } + `); + expectDiagnostics(diagnostics, { + code: "@typespec/versioning/no-service-fixed-version", + severity: "error", + }); + }); +}); + describe("versioning: validate incompatible references", () => { let runner: BasicTestRunner; let host: TestHost;