From 3fdd0bc08d6434a5a80caa3be4a7f7c73cd45588 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Wed, 8 Nov 2023 08:44:47 +0530 Subject: [PATCH 01/12] add extensions to the spec json schemas --- extensions/0.1.0/info.json | 16 ++++++++++++++++ tools/bundler/index.js | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 extensions/0.1.0/info.json diff --git a/extensions/0.1.0/info.json b/extensions/0.1.0/info.json new file mode 100644 index 00000000..90fc8ed2 --- /dev/null +++ b/extensions/0.1.0/info.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "additionalProperties": false, + "properties": { + "x-twitter": { + "type": "string", + "description": "This extension allows you to provide the Twitter username of the account representing the team/company of the API.", + "example": [ + "sambhavgupta75", + "AsyncAPISpec" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/extensions/0.1.0/info.json" +} \ No newline at end of file diff --git a/tools/bundler/index.js b/tools/bundler/index.js index 0ff04696..a3aa9036 100644 --- a/tools/bundler/index.js +++ b/tools/bundler/index.js @@ -4,10 +4,12 @@ const traverse = require('json-schema-traverse'); const { url } = require('inspector'); const definitionsDirectory = path.resolve(__dirname, '../../definitions'); const bindingsDirectory = path.resolve(__dirname, '../../bindings'); +const extensionsDirectory = path.resolve(__dirname, '../../extensions'); const outputDirectory = path.resolve(__dirname, '../../schemas'); const JSON_SCHEMA_PROP_NAME = 'json-schema-draft-07-schema'; console.log(`Looking for separate definitions in the following directory: ${definitionsDirectory}`); console.log(`Looking for binding version schemas in the following directory: ${bindingsDirectory}`); +console.log(`Looking for extension version schemas in the following directory: ${extensionsDirectory}`); console.log(`Using the following output directory: ${outputDirectory}`); // definitionsRegex is used to transform the name of a definition into a valid one to be used in the -without-$id.json files. From acb5bd7110f7cdb2fb1024d69799bdb404f18994 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Mon, 13 Nov 2023 11:05:43 +0530 Subject: [PATCH 02/12] renaming twitter to x Signed-off-by: Sambhav Gupta --- extensions/0.1.0/info.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/0.1.0/info.json b/extensions/0.1.0/info.json index 90fc8ed2..9c16238a 100644 --- a/extensions/0.1.0/info.json +++ b/extensions/0.1.0/info.json @@ -2,7 +2,7 @@ "type": "object", "additionalProperties": false, "properties": { - "x-twitter": { + "x-x": { "type": "string", "description": "This extension allows you to provide the Twitter username of the account representing the team/company of the API.", "example": [ From 55909645a9fc729bb75dde24b14291fbf9dd9499 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Thu, 21 Dec 2023 21:13:55 +0530 Subject: [PATCH 03/12] feat: added information of xx extension in info.json --- definitions/3.0.0/info.json | 3 +++ extensions/0.1.0/{info.json => x.json} | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) rename extensions/0.1.0/{info.json => x.json} (83%) diff --git a/definitions/3.0.0/info.json b/definitions/3.0.0/info.json index bf8c1491..5e7db79b 100644 --- a/definitions/3.0.0/info.json +++ b/definitions/3.0.0/info.json @@ -60,6 +60,9 @@ ] } }, + "x": { + "$ref": "http://asyncapi.com/extensions/0.1.0/x.json" + }, "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://asyncapi.com/definitions/3.0.0/info.json" } \ No newline at end of file diff --git a/extensions/0.1.0/info.json b/extensions/0.1.0/x.json similarity index 83% rename from extensions/0.1.0/info.json rename to extensions/0.1.0/x.json index 9c16238a..eb095c22 100644 --- a/extensions/0.1.0/info.json +++ b/extensions/0.1.0/x.json @@ -1,5 +1,4 @@ { - "type": "object", "additionalProperties": false, "properties": { "x-x": { @@ -12,5 +11,5 @@ } }, "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://asyncapi.com/extensions/0.1.0/info.json" + "$id": "http://asyncapi.com/extensions/0.1.0/x.json" } \ No newline at end of file From 0e9d788ea2db543acedcef60f2b57ef678bc0fb8 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Thu, 21 Dec 2023 21:24:37 +0530 Subject: [PATCH 04/12] feat: added x to info.json --- definitions/3.0.0/info.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/definitions/3.0.0/info.json b/definitions/3.0.0/info.json index 6c9b70fa..e3f1462a 100644 --- a/definitions/3.0.0/info.json +++ b/definitions/3.0.0/info.json @@ -61,6 +61,9 @@ ] } }, + "x":{ + "$ref": "http://asyncapi.com/extensions/0.1.0/x.json" + }, "example": { "$ref": "http://asyncapi.com/examples/3.0.0/info.json" }, From 43f7070f0f3af401331e22c22c703080498fdb86 Mon Sep 17 00:00:00 2001 From: derberg Date: Wed, 24 Jan 2024 14:15:00 +0100 Subject: [PATCH 05/12] chore: adjustments --- definitions/3.0.0/info.json | 121 +++++++++++++------------- definitions/3.0.0/infoExtensions.json | 11 +++ extensions/0.1.0/x.json | 15 ---- extensions/x/0.1.0/schema.json | 10 +++ tools/bundler/index.js | 51 ++++++++--- 5 files changed, 120 insertions(+), 88 deletions(-) create mode 100644 definitions/3.0.0/infoExtensions.json delete mode 100644 extensions/0.1.0/x.json create mode 100644 extensions/x/0.1.0/schema.json diff --git a/definitions/3.0.0/info.json b/definitions/3.0.0/info.json index e3f1462a..034eb5ab 100644 --- a/definitions/3.0.0/info.json +++ b/definitions/3.0.0/info.json @@ -1,69 +1,70 @@ { - "type": "object", "description": "The object provides metadata about the API. The metadata can be used by the clients if needed.", - "required": [ - "version", - "title" - ], - "additionalProperties": false, - "patternProperties": { - "^x-[\\w\\d\\.\\x2d_]+$": { - "$ref": "http://asyncapi.com/definitions/3.0.0/specificationExtension.json" - } - }, - "properties": { - "title": { - "type": "string", - "description": "A unique and precise title of the API." - }, - "version": { - "type": "string", - "description": "A semantic version number of the API." - }, - "description": { - "type": "string", - "description": "A longer description of the API. Should be different from the title. CommonMark is allowed." - }, - "termsOfService": { - "type": "string", - "description": "A URL to the Terms of Service for the API. MUST be in the format of a URL.", - "format": "uri" - }, - "contact": { - "$ref": "http://asyncapi.com/definitions/3.0.0/contact.json" - }, - "license": { - "$ref": "http://asyncapi.com/definitions/3.0.0/license.json" - }, - "tags": { - "type": "array", - "description": "A list of tags for application API documentation control. Tags can be used for logical grouping of applications.", - "items": { - "oneOf": [ - { - "$ref": "http://asyncapi.com/definitions/3.0.0/Reference.json" - }, - { - "$ref": "http://asyncapi.com/definitions/3.0.0/tag.json" - } - ] + "allOf": [ + { + "type": "object", + "required": ["version", "title"], + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\x2d_]+$": { + "$ref": "http://asyncapi.com/definitions/3.0.0/specificationExtension.json" + } }, - "uniqueItems": true - }, - "externalDocs": { - "oneOf": [ - { - "$ref": "http://asyncapi.com/definitions/3.0.0/Reference.json" + "properties": { + "title": { + "type": "string", + "description": "A unique and precise title of the API." }, - { - "$ref": "http://asyncapi.com/definitions/3.0.0/externalDocs.json" + "version": { + "type": "string", + "description": "A semantic version number of the API." + }, + "description": { + "type": "string", + "description": "A longer description of the API. Should be different from the title. CommonMark is allowed." + }, + "termsOfService": { + "type": "string", + "description": "A URL to the Terms of Service for the API. MUST be in the format of a URL.", + "format": "uri" + }, + "contact": { + "$ref": "http://asyncapi.com/definitions/3.0.0/contact.json" + }, + "license": { + "$ref": "http://asyncapi.com/definitions/3.0.0/license.json" + }, + "tags": { + "type": "array", + "description": "A list of tags for application API documentation control. Tags can be used for logical grouping of applications.", + "items": { + "oneOf": [ + { + "$ref": "http://asyncapi.com/definitions/3.0.0/Reference.json" + }, + { + "$ref": "http://asyncapi.com/definitions/3.0.0/tag.json" + } + ] + }, + "uniqueItems": true + }, + "externalDocs": { + "oneOf": [ + { + "$ref": "http://asyncapi.com/definitions/3.0.0/Reference.json" + }, + { + "$ref": "http://asyncapi.com/definitions/3.0.0/externalDocs.json" + } + ] } - ] + } + }, + { + "$ref": "http://asyncapi.com/definitions/3.0.0/infoExtensions.json" } - }, - "x":{ - "$ref": "http://asyncapi.com/extensions/0.1.0/x.json" - }, + ], "example": { "$ref": "http://asyncapi.com/examples/3.0.0/info.json" }, diff --git a/definitions/3.0.0/infoExtensions.json b/definitions/3.0.0/infoExtensions.json new file mode 100644 index 00000000..12d739ba --- /dev/null +++ b/definitions/3.0.0/infoExtensions.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "description": "The object that lists all the extensions of Info", + "properties": { + "x-x":{ + "$ref": "http://asyncapi.com/extensions/x/0.1.0/schema.json" + } + }, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/definitions/3.0.0/infoExtensions.json" +} \ No newline at end of file diff --git a/extensions/0.1.0/x.json b/extensions/0.1.0/x.json deleted file mode 100644 index eb095c22..00000000 --- a/extensions/0.1.0/x.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "additionalProperties": false, - "properties": { - "x-x": { - "type": "string", - "description": "This extension allows you to provide the Twitter username of the account representing the team/company of the API.", - "example": [ - "sambhavgupta75", - "AsyncAPISpec" - ] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://asyncapi.com/extensions/0.1.0/x.json" -} \ No newline at end of file diff --git a/extensions/x/0.1.0/schema.json b/extensions/x/0.1.0/schema.json new file mode 100644 index 00000000..e3f822a2 --- /dev/null +++ b/extensions/x/0.1.0/schema.json @@ -0,0 +1,10 @@ +{ + "type": "string", + "description": "This extension allows you to provide the Twitter username of the account representing the team/company of the API.", + "example": [ + "sambhavgupta75", + "AsyncAPISpec" + ], + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/extensions/x/0.1.0/schema.json" +} \ No newline at end of file diff --git a/tools/bundler/index.js b/tools/bundler/index.js index a3aa9036..2975a20b 100644 --- a/tools/bundler/index.js +++ b/tools/bundler/index.js @@ -18,6 +18,9 @@ const definitionsRegex = /http:\/\/asyncapi\.com\/definitions\/[^/]*\/(.+)\.json // definitionsRegex is used to transform the name of a binding into a valid one to be used in the -without-$id.json files. const bindingsRegex = /http:\/\/asyncapi\.com\/(bindings\/[^/]+)\/([^/]+)\/(.+)\.json(.*)/i +// definitionsRegex is used to transform the name of a binding into a valid one to be used in the -without-$id.json files. +const extensionsRegex = /http:\/\/asyncapi\.com\/(extensions\/[^/]+)\/([^/]+)\/(.+)\.json(.*)/i + /** * Function to load all the core AsyncAPI spec definition (except the root asyncapi schema, as that will be loaded later) into the bundler. */ @@ -25,6 +28,7 @@ async function loadDefinitions(bundler, versionDir) { const definitions = await fs.promises.readdir(versionDir); const definitionFiles = definitions.filter((value) => {return !value.includes('asyncapi')}).map((file) => fs.readFileSync(path.resolve(versionDir, file))); const definitionJson = definitionFiles.map((file) => JSON.parse(file)); + for (const jsonFile of definitionJson) { if (jsonFile.example) { // Replaced the example property with the referenced example property @@ -40,20 +44,36 @@ async function loadDefinitions(bundler, versionDir) { } } } + + /** - * Function to load all the binding version schemas into the bundler + * Function to load all schemas into bundler, by "type" you specify if these are "bindings" or "extensions" */ -async function loadBindings(bundler) { - const bindingDirectories = await fs.promises.readdir(bindingsDirectory); - for (const bindingDirectory of bindingDirectories) { - const bindingVersionDirectories = await fs.promises.readdir(path.resolve(bindingsDirectory, bindingDirectory)); - const bindingVersionDirectoriesFiltered = bindingVersionDirectories.filter((file) => fs.lstatSync(path.resolve(bindingsDirectory, bindingDirectory, file)).isDirectory()); - for (const bindingVersionDirectory of bindingVersionDirectoriesFiltered) { - const bindingFiles = await fs.promises.readdir(path.resolve(bindingsDirectory, bindingDirectory, bindingVersionDirectory)); - const bindingFilesFiltered = bindingFiles.filter((bindingFile) => path.extname(bindingFile) === '.json').map((bindingFile) => path.resolve(bindingsDirectory, bindingDirectory, bindingVersionDirectory, bindingFile)); - for (const bindingFile of bindingFilesFiltered) { - const bindingFileContent = require(bindingFile); - bundler.add(bindingFileContent); +async function loadSchemas(bundler, type) { + + let directory; + + switch (type) { + case "bindings": + directory = bindingsDirectory; + break; + case "extensions": + directory = extensionsDirectory; + break; + default: + console.error("Invalid input. I'm not going to assume if you want bindings or extensions - these are different beasts."); + } + + const directories = await fs.promises.readdir(directory); + for (const nestedDir of directories) { + const versionDirectories = await fs.promises.readdir(path.resolve(directory, nestedDir)); + const versionDirectoriesFiltered = versionDirectories.filter((file) => fs.lstatSync(path.resolve(directory, nestedDir, file)).isDirectory()); + for (const versionDir of versionDirectoriesFiltered) { + const files = await fs.promises.readdir(path.resolve(directory, nestedDir, versionDir)); + const filesFiltered = files.filter((file) => path.extname(file) === '.json').map((file) => path.resolve(directory, nestedDir, versionDir, file)); + for (const filteredFile of filesFiltered) { + const fileContent = require(filteredFile); + bundler.add(fileContent); } } } @@ -76,7 +96,8 @@ async function loadBindings(bundler) { const outputFileWithoutId = path.resolve(outputDirectory, `${version}-without-$id.json`); const versionDir = path.resolve(definitionsDirectory, version); await loadDefinitions(Bundler, versionDir); - await loadBindings(Bundler); + await loadSchemas(Bundler, 'bindings'); + await loadSchemas(Bundler, 'extensions'); const filePathToBundle = `file://${versionDir}/asyncapi.json`; const fileToBundle = await Bundler.get(filePathToBundle); @@ -157,6 +178,10 @@ function getDefinitionName(def) { const result = bindingsRegex.exec(def); if (result) return `${result[1].replace('/', '-')}-${result[2]}-${result[3]}`; } + if (def.startsWith('http://asyncapi.com/extensions')) { + const result = extensionsRegex.exec(def); + if (result) return `${result[1].replace('/', '-')}-${result[2]}-${result[3]}`; + } return path.basename(def, '.json') } From d987c1dc2127c94ca8c01d7ce43a073b4b4d3265 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Mon, 12 Feb 2024 23:06:23 +0530 Subject: [PATCH 06/12] added sonarcloud properties --- .sonarcloud.properties | 1 + 1 file changed, 1 insertion(+) create mode 100644 .sonarcloud.properties diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 00000000..0657ce63 --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1 @@ +sonar.exclusions=test/**/* \ No newline at end of file From 0b031b0f1b807881e30d5f56ba40951de71f2f49 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Wed, 14 Feb 2024 00:04:37 +0530 Subject: [PATCH 07/12] changed the sonar exclusions to tools --- .sonarcloud.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.sonarcloud.properties b/.sonarcloud.properties index 0657ce63..4cc6c5a3 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1 +1 @@ -sonar.exclusions=test/**/* \ No newline at end of file +sonar.exclusions=tools/**/* \ No newline at end of file From 138cb04d254336fe11071ca1d116b8380bb779c5 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta Date: Thu, 22 Feb 2024 17:17:35 +0530 Subject: [PATCH 08/12] added the extensions information --- .sonarcloud.properties | 2 +- README.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.sonarcloud.properties b/.sonarcloud.properties index 4cc6c5a3..20b124bc 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1 +1 @@ -sonar.exclusions=tools/**/* \ No newline at end of file +sonar.exclusions=tools/**/* diff --git a/README.md b/README.md index 0eb6ef1e..8c604d24 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,8 @@ This is the current project structure explained: - [./examples](./examples) - contain most individual definition examples that will automatically be bundled together to provide example for each definition in the schemas in [./schemas](./schemas). - [./tools/bundler](./tools/bundler) - is the tool that bundles all the individual schemas together. - [./schemas](./schemas) - contain all automatically bundled and complete schemas for each AsyncAPI version. These schemas should **NOT** be manually changed as they are automatically generated. Any changes should be done in [./definitions](./definitions). +- [./extensions](./extensions) - contains all the schemas of the extensions that will automatically be bundled to provide informations about extensions. + ## Schema Bundling @@ -210,7 +212,17 @@ Whenever you make changes in AsyncAPI JSON Schema, you should always manually ve ```yaml # yaml-language-server: $schema=YOUR-PROJECTS-DIRECTORY/spec-json-schemas/schemas/2.6.0-without-$id.json ``` + +## Extensions +The Extension Catalogue schema in AsyncAPI defines a standardized way to include additional metadata or custom attributes within an AsyncAPI document. +### How to add extensions +There is defined structure of adding the exrensions which is defined as followed: +1. All the extensions must be present in [./extensions](./extensions) folder. +2. A proper folder structure must be followed to add the extensions. +3. A new folder just as [x extension](./extensions/x) must be added with proper `versioning` and `schema file`. +4. All the schemas must be added in a fle named `schema.json` just as one is defined for [x extension](./extensions/x/0.1.0/schema.json). + From d4af5569dcad371e05318f7766856278c4208a68 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta <81870866+sambhavgupta0705@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:28:23 +0530 Subject: [PATCH 09/12] Update README.md Co-authored-by: Lukasz Gornicki --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c604d24..cb85300c 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,6 @@ There is defined structure of adding the exrensions which is defined as followed 3. A new folder just as [x extension](./extensions/x) must be added with proper `versioning` and `schema file`. 4. All the schemas must be added in a fle named `schema.json` just as one is defined for [x extension](./extensions/x/0.1.0/schema.json). - +5. Extension schema should not be referenced directly in the definition of the object it extends. For example if you add an extension for `info`, your extension's schema should not be referenced from `info.json` but [infoExtensions.json](./definitions/3.0.0/infoExtensions.json). If the object that you extend doesn't have a corresponding `*Extensions.json` file, you need to create one. From 28adfe0dcc1914b411081c2d6f204fc1c1878ac5 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta <81870866+sambhavgupta0705@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:28:32 +0530 Subject: [PATCH 10/12] Update README.md Co-authored-by: Lukasz Gornicki --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb85300c..09d1135d 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ There is defined structure of adding the exrensions which is defined as followed 1. All the extensions must be present in [./extensions](./extensions) folder. 2. A proper folder structure must be followed to add the extensions. 3. A new folder just as [x extension](./extensions/x) must be added with proper `versioning` and `schema file`. -4. All the schemas must be added in a fle named `schema.json` just as one is defined for [x extension](./extensions/x/0.1.0/schema.json). +4. All the schemas must be added in a file named `schema.json` just as one is defined for [x extension](./extensions/x/0.1.0/schema.json). 5. Extension schema should not be referenced directly in the definition of the object it extends. For example if you add an extension for `info`, your extension's schema should not be referenced from `info.json` but [infoExtensions.json](./definitions/3.0.0/infoExtensions.json). If the object that you extend doesn't have a corresponding `*Extensions.json` file, you need to create one. From 1fa90047f80bb7a3df4fe701a47214f5a7d17eac Mon Sep 17 00:00:00 2001 From: Sambhav Gupta <81870866+sambhavgupta0705@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:28:40 +0530 Subject: [PATCH 11/12] Update README.md Co-authored-by: Lukasz Gornicki --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 09d1135d..67d5a9a0 100644 --- a/README.md +++ b/README.md @@ -215,8 +215,8 @@ Whenever you make changes in AsyncAPI JSON Schema, you should always manually ve ## Extensions The Extension Catalogue schema in AsyncAPI defines a standardized way to include additional metadata or custom attributes within an AsyncAPI document. -### How to add extensions -There is defined structure of adding the exrensions which is defined as followed: +### How to add schema of the extension + 1. All the extensions must be present in [./extensions](./extensions) folder. 2. A proper folder structure must be followed to add the extensions. From 5f0319648b64163bbbd2f6c62e401a531f6fdae2 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta <81870866+sambhavgupta0705@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:28:47 +0530 Subject: [PATCH 12/12] Update README.md Co-authored-by: Lukasz Gornicki --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 67d5a9a0..5d5de24a 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,8 @@ Whenever you make changes in AsyncAPI JSON Schema, you should always manually ve ``` ## Extensions -The Extension Catalogue schema in AsyncAPI defines a standardized way to include additional metadata or custom attributes within an AsyncAPI document. + +Extensions are a way to [extend AsyncAPI specification](https://www.asyncapi.com/docs/concepts/asyncapi-document/extending-specification) with fields that are not yet defined inside the specification. To add JSON schema of the extension in this repository, you need to first make sure it is added to the [extension-catalog](https://github.com/asyncapi/extensions-catalog) repository. ### How to add schema of the extension