From 411700a90867329d0fdd2a4d8c263f7bae527fc1 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 26 Jun 2023 20:06:23 -0700 Subject: [PATCH] Add docs for JSON Schema (#2120) --- .../json-schema-docs_2023-06-24-10-36.json | 10 + common/config/rush/pnpm-lock.yaml | 23 +- .../json-schema/reference/data-types.md | 32 ++ .../json-schema/reference/decorators.md | 293 ++++++++++++++++++ .../json-schema/reference/emitter.md | 57 ++++ .../json-schema/reference/index.md | 58 ++++ packages/json-schema/lib/main.tsp | 9 +- packages/website/.scripts/regen-ref-docs.mjs | 10 + packages/website/package.json | 1 + packages/website/sidebars.js | 2 +- 10 files changed, 482 insertions(+), 13 deletions(-) create mode 100644 common/changes/@typespec/json-schema/json-schema-docs_2023-06-24-10-36.json create mode 100644 docs/standard-library/json-schema/reference/data-types.md create mode 100644 docs/standard-library/json-schema/reference/decorators.md create mode 100644 docs/standard-library/json-schema/reference/emitter.md create mode 100644 docs/standard-library/json-schema/reference/index.md diff --git a/common/changes/@typespec/json-schema/json-schema-docs_2023-06-24-10-36.json b/common/changes/@typespec/json-schema/json-schema-docs_2023-06-24-10-36.json new file mode 100644 index 0000000000..c15d89290f --- /dev/null +++ b/common/changes/@typespec/json-schema/json-schema-docs_2023-06-24-10-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/json-schema", + "comment": "", + "type": "none" + } + ], + "packageName": "@typespec/json-schema" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index fafb5afbf4..0122f0bca9 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1429,6 +1429,9 @@ importers: '@typespec/http': specifier: workspace:~0.45.0 version: link:../http + '@typespec/json-schema': + specifier: workspace:~0.45.0 + version: link:../json-schema '@typespec/openapi': specifier: workspace:~0.45.0 version: link:../openapi @@ -3105,9 +3108,9 @@ packages: node-watch: 0.7.1 picocolors: 1.0.0 prettier: 2.7.1 - prompts: 2.4.1 + prompts: 2.4.2 vscode-languageserver: 7.0.0 - vscode-languageserver-textdocument: 1.0.1 + vscode-languageserver-textdocument: 1.0.8 yargs: 17.3.1 dev: false @@ -3127,9 +3130,9 @@ packages: node-watch: 0.7.1 picocolors: 1.0.0 prettier: 2.7.1 - prompts: 2.4.1 + prompts: 2.4.2 vscode-languageserver: 8.0.2 - vscode-languageserver-textdocument: 1.0.1 + vscode-languageserver-textdocument: 1.0.8 yargs: 17.6.2 dev: false @@ -3149,9 +3152,9 @@ packages: node-watch: 0.7.1 picocolors: 1.0.0 prettier: 2.8.7 - prompts: 2.4.1 + prompts: 2.4.2 vscode-languageserver: 8.0.2 - vscode-languageserver-textdocument: 1.0.1 + vscode-languageserver-textdocument: 1.0.8 yargs: 17.6.2 dev: false @@ -6812,9 +6815,9 @@ packages: node-watch: 0.7.1 picocolors: 1.0.0 prettier: 2.8.7 - prompts: 2.4.1 + prompts: 2.4.2 vscode-languageserver: 8.0.2 - vscode-languageserver-textdocument: 1.0.1 + vscode-languageserver-textdocument: 1.0.8 yargs: 17.6.2 dev: false @@ -6834,9 +6837,9 @@ packages: node-watch: 0.7.1 picocolors: 1.0.0 prettier: 2.8.7 - prompts: 2.4.1 + prompts: 2.4.2 vscode-languageserver: 8.0.2 - vscode-languageserver-textdocument: 1.0.1 + vscode-languageserver-textdocument: 1.0.8 yargs: 17.6.2 dev: false diff --git a/docs/standard-library/json-schema/reference/data-types.md b/docs/standard-library/json-schema/reference/data-types.md new file mode 100644 index 0000000000..e07738cf02 --- /dev/null +++ b/docs/standard-library/json-schema/reference/data-types.md @@ -0,0 +1,32 @@ +--- +title: "Data types" +toc_min_heading_level: 2 +toc_max_heading_level: 3 +--- + +# Data types + +## TypeSpec.JsonSchema + +### `Json` {#TypeSpec.JsonSchema.Json} + +Specify that the provided template argument should be emitted as raw JSON or YAML +as opposed to a schema. Use in combination with the + +```typespec +model TypeSpec.JsonSchema.Json +``` + +#### Template Parameters + +| Name | Description | +| ---- | ------------------------------- | +| T | the type to convert to raw JSON | + +### `Format` {#TypeSpec.JsonSchema.Format} + +Well-known JSON Schema formats. + +```typespec +enum TypeSpec.JsonSchema.Format +``` diff --git a/docs/standard-library/json-schema/reference/decorators.md b/docs/standard-library/json-schema/reference/decorators.md new file mode 100644 index 0000000000..d039d2027f --- /dev/null +++ b/docs/standard-library/json-schema/reference/decorators.md @@ -0,0 +1,293 @@ +--- +title: "Decorators" +toc_min_heading_level: 2 +toc_max_heading_level: 3 +--- + +# Decorators + +## TypeSpec.JsonSchema + +### `@baseUri` {#@TypeSpec.JsonSchema.baseUri} + +Set the base URI for any schemas emitted from types within this namespace. + +```typespec +@TypeSpec.JsonSchema.baseUri(baseUri: valueof string) +``` + +#### Target + +`Namespace` + +#### Parameters + +| Name | Type | Description | +| ------- | ----------------------- | ------------------------------------------------------------------------ | +| baseUri | `valueof scalar string` | the base URI. Schema IDs inside this namespace are relative to this URI. | + +### `@contains` {#@TypeSpec.JsonSchema.contains} + +Specify that the array must contain at least one instance of the provided type. +Use `@minContains` and `@maxContains` to customize how many instances to expect. + +```typespec +@TypeSpec.JsonSchema.contains(value: unknown) +``` + +#### Target + +`union unknown[] | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | --------------------- | -------------------------------- | +| value | `(intrinsic) unknown` | The type the array must contain. | + +### `@contentEncoding` {#@TypeSpec.JsonSchema.contentEncoding} + +Specify the encoding used for the contents of a string. + +```typespec +@TypeSpec.JsonSchema.contentEncoding(value: valueof string) +``` + +#### Target + +`union string | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ----------------------- | ----------- | +| value | `valueof scalar string` |
| + +### `@contentMediaType` {#@TypeSpec.JsonSchema.contentMediaType} + +Specify the content type of content stored in a string. + +```typespec +@TypeSpec.JsonSchema.contentMediaType(value: valueof string) +``` + +#### Target + +`union string | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ----------------------- | ------------------------------------- | +| value | `valueof scalar string` | the media type of the string contents | + +### `@contentSchema` {#@TypeSpec.JsonSchema.contentSchema} + +Specify the schema for the contents of a string when interpreted according to the content's +media type and encoding. + +```typespec +@TypeSpec.JsonSchema.contentSchema(value: unknown) +``` + +#### Target + +`union string | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | --------------------- | --------------------------------- | +| value | `(intrinsic) unknown` | the schema of the string contents | + +### `@extension` {#@TypeSpec.JsonSchema.extension} + +Specify a custom property to add to the emitted schema. Useful for adding custom keywords +and other vendor-specific extensions. The value will be converted to a schema unless the parameter +is wrapped in the `Json` template. For example, `@extension("x-schema", { x: "value" })` will +emit a JSON schema value for `x-schema`, whereas `@extension("x-schema", Json<{x: "value"}>)` will +emit the raw JSON code `{x: "value"}`. + +```typespec +@TypeSpec.JsonSchema.extension(key: valueof string, value: unknown) +``` + +#### Target + +`(intrinsic) unknown` + +#### Parameters + +| Name | Type | Description | +| ----- | ----------------------- | ------------------------------------------------------------------------------------ | +| key | `valueof scalar string` | the name of the keyword of vendor extension, e.g. `x-custom`. | +| value | `(intrinsic) unknown` | the value of the keyword. Will be converted to a schema unless wrapped in `Json`. | + +### `@id` {#@TypeSpec.JsonSchema.id} + +Specify the JSON Schema id. If this model or a parent namespace has a base URI, +the provided ID will be relative to that base URI. + +By default, the id will be constructed based on the declaration's name. + +```typespec +@TypeSpec.JsonSchema.id(id: valueof string) +``` + +#### Target + +`(intrinsic) unknown` + +#### Parameters + +| Name | Type | Description | +| ---- | ----------------------- | ----------------------------------------------- | +| id | `valueof scalar string` | the id of the JSON schema for this declaration. | + +### `@jsonSchema` {#@TypeSpec.JsonSchema.jsonSchema} + +Add to namespaces to emit models within that namespace to JSON schema. +Add to another declaration to emit that declaration to JSON schema. + +Optionally, for namespaces, you can provide a baseUri, and for other declarations, +you can provide the id. + +```typespec +@TypeSpec.JsonSchema.jsonSchema(baseUri?: valueof string) +``` + +#### Target + +`(intrinsic) unknown` + +#### Parameters + +| Name | Type | Description | +| ------- | ----------------------- | --------------------------------------------------- | +| baseUri | `valueof scalar string` | Schema IDs are interpreted as relative to this URI. | + +### `@maxContains` {#@TypeSpec.JsonSchema.maxContains} + +Specify that the array must contain at most some number of the types provided +by the contains decorator. + +```typespec +@TypeSpec.JsonSchema.maxContains(value: valueof int32) +``` + +#### Target + +`union unknown[] | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ---------------------- | ------------------------------------------------------ | +| value | `valueof scalar int32` | The maximum number of instances the array must contain | + +### `@maxProperties` {#@TypeSpec.JsonSchema.maxProperties} + +Specify the maximum number of properties this object can have. + +```typespec +@TypeSpec.JsonSchema.maxProperties(value: valueof int32) +``` + +#### Target + +`union Record | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ---------------------- | ------------------------------------------------------ | +| value | `valueof scalar int32` | The maximum number of properties this object can have. | + +### `@minContains` {#@TypeSpec.JsonSchema.minContains} + +Specify that the array must contain at least some number of the types provided +by the contains decorator. + +```typespec +@TypeSpec.JsonSchema.minContains(value: valueof int32) +``` + +#### Target + +`union unknown[] | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ---------------------- | ------------------------------------------------------ | +| value | `valueof scalar int32` | The minimum number of instances the array must contain | + +### `@minProperties` {#@TypeSpec.JsonSchema.minProperties} + +Specify the minimum number of properties this object can have. + +```typespec +@TypeSpec.JsonSchema.minProperties(value: valueof int32) +``` + +#### Target + +`union Record | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ---------------------- | ------------------------------------------------------ | +| value | `valueof scalar int32` | The minimum number of properties this object can have. | + +### `@multipleOf` {#@TypeSpec.JsonSchema.multipleOf} + +Specify that the numeric type must be a multiple of some numeric value. + +```typespec +@TypeSpec.JsonSchema.multipleOf(value: valueof numeric) +``` + +#### Target + +`union numeric | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ------------------------ | -------------------------------------------------- | +| value | `valueof scalar numeric` | The numeric type must be a multiple of this value. | + +### `@prefixItems` {#@TypeSpec.JsonSchema.prefixItems} + +Specify that the target array must begin with the provided types. + +```typespec +@TypeSpec.JsonSchema.prefixItems(value: unknown[]) +``` + +#### Target + +`union unknown[] | ModelProperty` + +#### Parameters + +| Name | Type | Description | +| ----- | ----------------- | --------------------------------------------------------------------------- | +| value | `model unknown[]` | a tuple containing the types that must be present at the start of the array | + +### `@uniqueItems` {#@TypeSpec.JsonSchema.uniqueItems} + +Specify that every item in the array must be unique. + +```typespec +@TypeSpec.JsonSchema.uniqueItems +``` + +#### Target + +`union unknown[] | ModelProperty` + +#### Parameters + +None diff --git a/docs/standard-library/json-schema/reference/emitter.md b/docs/standard-library/json-schema/reference/emitter.md new file mode 100644 index 0000000000..0fde819793 --- /dev/null +++ b/docs/standard-library/json-schema/reference/emitter.md @@ -0,0 +1,57 @@ +--- +title: "Emitter usage" +toc_min_heading_level: 2 +toc_max_heading_level: 3 +--- + +# Emitter usage + +## Usage + +1. Via the command line + +```bash +tsp compile . --emit=@typespec/json-schema +``` + +2. Via the config + +```yaml +emit: + - "@typespec/json-schema" +``` + +## Emitter options + +### `file-type` + +**Type:** `"yaml" | "json"` + +Serialize the schema as either yaml or json. + +### `int64-strategy` + +**Type:** `"string" | "number"` + +How to handle 64 bit integers on the wire. Options are: + +- string: serialize as a string (widely interoperable) +- number: serialize as a number (not widely interoperable) + +### `bundleId` + +**Type:** `string` + +When provided, bundle all the schemas into a single json schema document with schemas under $defs. The provided id is the id of the root document and is also used for the file name. + +### `emitAllModels` + +**Type:** `boolean` + +When true, emit all model declarations to JSON Schema without requiring the @jsonSchema decorator. + +### `emitAllRefs` + +**Type:** `boolean` + +When true, emit all references as json schema files, even if the referenced type does not have the `@jsonSchema` decorator or is not within a namespace with the `@jsonSchema` decorator. diff --git a/docs/standard-library/json-schema/reference/index.md b/docs/standard-library/json-schema/reference/index.md new file mode 100644 index 0000000000..d405fb8e02 --- /dev/null +++ b/docs/standard-library/json-schema/reference/index.md @@ -0,0 +1,58 @@ +--- +title: Overview +sidebar_position: 0 +toc_min_heading_level: 2 +toc_max_heading_level: 3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +TypeSpec library for emitting TypeSpec to JSON Schema and converting JSON Schema to TypeSpec + +## Install + + + + +```bash +npm install @typespec/json-schema +``` + + + + +```bash +npm install --save-peer @typespec/json-schema +``` + + + + +### Emitter usage + +[See documentation](./emitter.md) + +## TypeSpec.JsonSchema + +### Decorators + +- [`@baseUri`](./decorators.md#@TypeSpec.JsonSchema.baseUri) +- [`@contains`](./decorators.md#@TypeSpec.JsonSchema.contains) +- [`@contentEncoding`](./decorators.md#@TypeSpec.JsonSchema.contentEncoding) +- [`@contentMediaType`](./decorators.md#@TypeSpec.JsonSchema.contentMediaType) +- [`@contentSchema`](./decorators.md#@TypeSpec.JsonSchema.contentSchema) +- [`@extension`](./decorators.md#@TypeSpec.JsonSchema.extension) +- [`@id`](./decorators.md#@TypeSpec.JsonSchema.id) +- [`@jsonSchema`](./decorators.md#@TypeSpec.JsonSchema.jsonSchema) +- [`@maxContains`](./decorators.md#@TypeSpec.JsonSchema.maxContains) +- [`@maxProperties`](./decorators.md#@TypeSpec.JsonSchema.maxProperties) +- [`@minContains`](./decorators.md#@TypeSpec.JsonSchema.minContains) +- [`@minProperties`](./decorators.md#@TypeSpec.JsonSchema.minProperties) +- [`@multipleOf`](./decorators.md#@TypeSpec.JsonSchema.multipleOf) +- [`@prefixItems`](./decorators.md#@TypeSpec.JsonSchema.prefixItems) +- [`@uniqueItems`](./decorators.md#@TypeSpec.JsonSchema.uniqueItems) + +### Models + +- [`Json`](./data-types.md#TypeSpec.JsonSchema.Json) diff --git a/packages/json-schema/lib/main.tsp b/packages/json-schema/lib/main.tsp index 7da4e3ad48..4da3009ea4 100644 --- a/packages/json-schema/lib/main.tsp +++ b/packages/json-schema/lib/main.tsp @@ -96,7 +96,7 @@ extern dec prefixItems(target: unknown[] | Reflection.ModelProperty, value: unkn /** * Specify the content type of content stored in a string. * - * @param the media type of the string contents + * @param value the media type of the string contents * */ extern dec contentMediaType(target: string | Reflection.ModelProperty, value: valueof string); @@ -117,10 +117,13 @@ extern dec contentSchema(target: string | Reflection.ModelProperty, value: unkno * emit the raw JSON code `{x: "value"}`. * * @param key the name of the keyword of vendor extension, e.g. `x-custom`. - * @param value the value of the keyword. Will be converted to a schema unless wrapped in Json. + * @param value the value of the keyword. Will be converted to a schema unless wrapped in `Json`. */ extern dec extension(target: unknown, key: valueof string, value: unknown); +/** + * Well-known JSON Schema formats. + */ enum Format { dateTime: "date-time", date: "date", @@ -148,6 +151,8 @@ enum Format { * `@extension("x-schema", { x: "value" })` will emit a JSON schema value for `x-schema`, * whereas `@extension("x-schema", Json<{x: "value"}>)` will emit the raw JSON code * `{x: "value"}`. + * + * @template T the type to convert to raw JSON */ @Private.validatesRawJson(T) model Json { diff --git a/packages/website/.scripts/regen-ref-docs.mjs b/packages/website/.scripts/regen-ref-docs.mjs index 5ebc41815e..64af0a3a4c 100644 --- a/packages/website/.scripts/regen-ref-docs.mjs +++ b/packages/website/.scripts/regen-ref-docs.mjs @@ -73,6 +73,16 @@ if (protobufDiag.length) { diagnostics.set("@typespec/protobuf", protobufDiag); } +// JSON Schema +const jsonSchema = await generateLibraryDocs( + join(repoRoot, "packages/json-schema"), + ["TypeSpec.JsonSchema"], + join(repoRoot, "docs/standard-library/json-schema/reference") +); +if (jsonSchema.length) { + diagnostics.set("@typespec/json-schema", jsonSchema); +} + // Versioning const versioningDiag = await generateLibraryDocs( join(repoRoot, "packages/versioning"), diff --git a/packages/website/package.json b/packages/website/package.json index 839193ab26..ed72f65b34 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -40,6 +40,7 @@ "@typespec/openapi3": "workspace:~0.45.0", "@typespec/protobuf": "workspace:~0.44.0", "@typespec/versioning": "workspace:~0.45.0", + "@typespec/json-schema": "workspace:~0.45.0", "@docusaurus/module-type-aliases": "^2.2.0", "@docusaurus/types": "^2.2.0", "@tsconfig/docusaurus": "^1.0.5", diff --git a/packages/website/sidebars.js b/packages/website/sidebars.js index 8320acaed2..bddc4836bc 100644 --- a/packages/website/sidebars.js +++ b/packages/website/sidebars.js @@ -88,7 +88,7 @@ const sidebars = { "standard-library/http/authentication", "standard-library/http/operations", ]), - + createLibraryReferenceStructure("json-schema", "JSON Schema", []), createLibraryReferenceStructure("rest", "Rest", [ "standard-library/rest/cheat-sheet", "standard-library/rest/resource-routing",