Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds public function for setting info object directly #3626

Merged
merged 27 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
af56e27
Add support for extending a namespace
irvinesunday Jun 20, 2024
1bcfb65
Add extension on namespace test
irvinesunday Jun 20, 2024
fa4fc96
Update name used in test
irvinesunday Jun 20, 2024
502c55c
Fix file with prettier
irvinesunday Jun 20, 2024
8630b2a
Merge branch 'main' into irvine/set-info-extension
irvinesunday Jun 20, 2024
74bc354
Add new method to fetch namespace extensions
irvinesunday Jun 27, 2024
58f871d
Merge branch 'irvine/set-info-extension' of https://github.com/irvine…
irvinesunday Jun 27, 2024
5a91849
Merge branch 'main' into irvine/set-info-extension
irvinesunday Jun 27, 2024
2ced469
Use prettier on file
irvinesunday Jun 27, 2024
1bdbfc1
Merge branch 'irvine/set-info-extension' of https://github.com/irvine…
irvinesunday Jun 27, 2024
c34bccf
Add documentation for new functions
irvinesunday Jun 27, 2024
8bfc8fb
Add changeset
irvinesunday Jul 2, 2024
8a80082
Remove setNamespaceExtension
irvinesunday Jul 3, 2024
a9daf14
Update test message
irvinesunday Jul 3, 2024
5dc9076
Merge branch 'main' into irvine/set-info-extension
irvinesunday Jul 3, 2024
85d49e3
PR review suggestion
irvinesunday Jul 4, 2024
5b341bd
Remove test
irvinesunday Jul 4, 2024
7d71da8
Remove unnecessary functions
irvinesunday Jul 4, 2024
4dd8198
Use prettier
irvinesunday Jul 4, 2024
e1eaadb
Update changeset
irvinesunday Jul 4, 2024
230b6a7
Remove unnecessary code; whitespace
irvinesunday Jul 4, 2024
09af6a9
Merge branch 'main' into irvine/set-info-extension
irvinesunday Jul 4, 2024
b10145f
Merge branch 'main' into irvine/set-info-extension
irvinesunday Jul 5, 2024
0d20f1b
Merge branch 'main' into irvine/set-info-extension
irvinesunday Jul 8, 2024
edce4c3
Update packages/openapi/src/decorators.ts
timotheeguerin Jul 8, 2024
f70941b
Format
timotheeguerin Jul 8, 2024
89c0931
Fix build
timotheeguerin Jul 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/openapi"
---

Adds support for extending namespace with extensions
42 changes: 37 additions & 5 deletions packages/openapi/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const $extension: ExtensionDecorator = (
extensionName: string,
value: TypeSpecValue
) => {
if (!isOpenAPIExtensionKey(extensionName)) {
if (entity.kind !== "Namespace" && !isOpenAPIExtensionKey(extensionName)) {
reportDiagnostic(context.program, {
code: "invalid-extension-key",
format: { value: extensionName },
Expand All @@ -66,13 +66,18 @@ export const $extension: ExtensionDecorator = (
if (diagnostics.length > 0) {
context.program.reportDiagnostics(diagnostics);
}
setExtension(context.program, entity, extensionName as ExtensionKey, data);

if (entity.kind === "Namespace") {
setExtension(context.program, entity, extensionName, data);
} else {
setExtension(context.program, entity, extensionName as ExtensionKey, data);
}
};

export function setExtension(
program: Program,
entity: Type,
extensionName: ExtensionKey,
extensionName: ExtensionKey | string,
data: unknown
) {
const openApiExtensions = program.stateMap(openApiExtensionKey);
Expand All @@ -86,6 +91,19 @@ export function getExtensions(program: Program, entity: Type): ReadonlyMap<Exten
return program.stateMap(openApiExtensionKey).get(entity) ?? new Map<ExtensionKey, any>();
}

/**
* Gets the extensions for a namespace.
* @param program global program
* @param entity the namespace
* @returns the extensions for the entity
*/
export function getNamespaceExtensions(
program: Program,
entity: Namespace
): ReadonlyMap<string, any> {
return program.stateMap(openApiExtensionKey).get(entity) ?? new Map<string, any>();
}

function isOpenAPIExtensionKey(key: string): key is ExtensionKey {
return key.startsWith("x-");
}
Expand Down Expand Up @@ -159,9 +177,23 @@ export const $info: InfoDecorator = (
}
context.program.stateMap(infoKey).set(entity, data);
};

/** The extension name to be used to extend `@info`. */
export const infoExtensionName = "infoExtension";
export function getInfo(program: Program, entity: Namespace): AdditionalInfo | undefined {
return program.stateMap(infoKey).get(entity);
const info = program.stateMap(infoKey).get(entity);
const extensionsMap = getNamespaceExtensions(program, entity);
const extensions = extensionsMap.get(infoExtensionName);

/* The following info properties can be provided via extensions and should be overridden */
if (extensions && info) {
if (Object.prototype.hasOwnProperty.call(extensions, "contact")) {
info.contact = null;
}
if (Object.prototype.hasOwnProperty.call(extensions, "title")) {
info.title = null;
timotheeguerin marked this conversation as resolved.
Show resolved Hide resolved
}
}
return omitUndefined({ ...info, ...extensions });
}

/** Resolve the info entry by merging data specified with `@service`, `@summary` and `@info`. */
Expand Down
33 changes: 31 additions & 2 deletions packages/openapi/test/decorators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,35 @@ describe("openapi: decorators", () => {
});
});

it("apply extension on namespace", async () => {
const { Service } = (await runner.compile(`
alias sample = {
"contact": {
"email": "ionutro@microsoft.com",
},
"title": "Sample Title",
"x-ai-description": "Sample description.",
"x-legal-info-url": "https://app.sample.ai/legal",
"x-logo": "https://th.bing.com/th?id=OSK.935650835684F7E18AC3F31034DE6DF3",
"x-privacy-policy-url": "https://app.sample.ai/privacy",
};
@extension("infoExtension", sample)
@test namespace Service {}
`)) as { Service: Namespace };

const info = getInfo(runner.program, Service);
deepStrictEqual(info, {
contact: {
email: "ionutro@microsoft.com",
},
title: "Sample Title",
"x-ai-description": "Sample description.",
"x-legal-info-url": "https://app.sample.ai/legal",
"x-logo": "https://th.bing.com/th?id=OSK.935650835684F7E18AC3F31034DE6DF3",
"x-privacy-policy-url": "https://app.sample.ai/privacy",
});
});

it("apply extension with complex value", async () => {
const { Foo } = await runner.compile(`
@extension("x-custom", {foo: 123, bar: "string"})
Expand All @@ -67,7 +96,7 @@ describe("openapi: decorators", () => {
});
});

it("emit diagnostics when passing non string extension key", async () => {
it("emit diagnostics when passing non string extension key on a non-namespace", async () => {
const diagnostics = await runner.diagnose(`
@extension(123, "Bar")
@test
Expand All @@ -81,7 +110,7 @@ describe("openapi: decorators", () => {
});
});

it("emit diagnostics when passing extension key not starting with `x-`", async () => {
it("emit diagnostics when passing extension key not starting with `x-` on a non-namespace", async () => {
const diagnostics = await runner.diagnose(`
@extension("foo", "Bar")
@test
Expand Down
Loading