From 60accc01615158ea480fb490201c724eece9823d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:08:04 +0100 Subject: [PATCH 1/4] chore(deps): bump path-to-regexp and express (#3449) --- package-lock.json | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87b500419b87..82080d33a35c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13722,9 +13722,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -13745,7 +13745,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -13760,6 +13760,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/debug": { @@ -23339,9 +23343,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/path-type": { "version": "4.0.0", From 949f5208fc425d0b10fbedbbc565a63a5b633477 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Fri, 6 Dec 2024 01:39:36 +0100 Subject: [PATCH 2/4] chore: update meetings.json and newsrooom_videos.json (#3451) --- config/meetings.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/config/meetings.json b/config/meetings.json index a6db3de6e5be..3b20641c3cd9 100644 --- a/config/meetings.json +++ b/config/meetings.json @@ -1,11 +1,4 @@ [ - { - "title": "Essential Building Blocks Working Group", - "calLink": "https://www.google.com/calendar/event?eid=c28zazMxcDk3MThpMWFpNG9lYzRrbmIzNW8gY19xOXRzZWlnbG9tZHNqNm5qdWh2YnB0czExY0Bn", - "url": "https://github.com/asyncapi/community/issues/1342", - "banner": "", - "date": "2024-08-27T18:00:00.000Z" - }, { "title": "Marketing WG Meeting", "calLink": "https://www.google.com/calendar/event?eid=MmpwdGxqb29wcHQyaGk3dXU1cTJ0M3E2aGMgY19xOXRzZWlnbG9tZHNqNm5qdWh2YnB0czExY0Bn", From 170e72f89f2f3fb96998bcc6cbf0e1021380011c Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Sat, 7 Dec 2024 13:18:34 +0100 Subject: [PATCH 3/4] docs(cli): update latest cli documentation (#3453) --- markdown/docs/tools/cli/usage.md | 56 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/markdown/docs/tools/cli/usage.md b/markdown/docs/tools/cli/usage.md index 84f59e8803c4..ca7bcce55ec2 100644 --- a/markdown/docs/tools/cli/usage.md +++ b/markdown/docs/tools/cli/usage.md @@ -27,7 +27,7 @@ $ npm install -g @asyncapi/cli $ asyncapi COMMAND running command... $ asyncapi (--version) -@asyncapi/cli/2.11.0 linux-x64 node-v18.20.5 +@asyncapi/cli/2.11.1 linux-x64 node-v18.20.5 $ asyncapi --help [COMMAND] USAGE $ asyncapi COMMAND @@ -101,7 +101,7 @@ EXAMPLES $ asyncapi bundle ./asyncapi.yaml -o final-asyncapi.yaml --base ../public-api/main.yaml --baseDir ./social-media/comments-service ``` -_See code: [src/commands/bundle.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/bundle.ts)_ +_See code: [src/commands/bundle.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/bundle.ts)_ ## `asyncapi config` @@ -115,7 +115,7 @@ DESCRIPTION CLI config settings ``` -_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/index.ts)_ +_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/index.ts)_ ## `asyncapi config analytics` @@ -135,7 +135,7 @@ DESCRIPTION Enable or disable analytics for metrics collection ``` -_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/analytics.ts)_ +_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/analytics.ts)_ ## `asyncapi config context` @@ -149,7 +149,7 @@ DESCRIPTION Manage short aliases for full paths to AsyncAPI documents ``` -_See code: [src/commands/config/context/index.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/index.ts)_ +_See code: [src/commands/config/context/index.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/index.ts)_ ## `asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH` @@ -171,7 +171,7 @@ DESCRIPTION Add a context to the store ``` -_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/add.ts)_ +_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/add.ts)_ ## `asyncapi config context current` @@ -188,7 +188,7 @@ DESCRIPTION Shows the current context that is being used ``` -_See code: [src/commands/config/context/current.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/current.ts)_ +_See code: [src/commands/config/context/current.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/current.ts)_ ## `asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH` @@ -209,7 +209,7 @@ DESCRIPTION Edit a context in the store ``` -_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/edit.ts)_ +_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/edit.ts)_ ## `asyncapi config context init [CONTEXT-FILE-PATH]` @@ -232,7 +232,7 @@ DESCRIPTION Initialize context ``` -_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/init.ts)_ +_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/init.ts)_ ## `asyncapi config context list` @@ -249,7 +249,7 @@ DESCRIPTION List all the stored contexts in the store ``` -_See code: [src/commands/config/context/list.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/list.ts)_ +_See code: [src/commands/config/context/list.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/list.ts)_ ## `asyncapi config context remove CONTEXT-NAME` @@ -269,7 +269,7 @@ DESCRIPTION Delete a context from the store ``` -_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/remove.ts)_ +_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/remove.ts)_ ## `asyncapi config context use CONTEXT-NAME` @@ -289,7 +289,7 @@ DESCRIPTION Set a context as current ``` -_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/context/use.ts)_ +_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/context/use.ts)_ ## `asyncapi config versions` @@ -306,7 +306,7 @@ DESCRIPTION Show versions of AsyncAPI tools used ``` -_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/config/versions.ts)_ +_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/config/versions.ts)_ ## `asyncapi convert [SPEC-FILE]` @@ -334,7 +334,7 @@ DESCRIPTION Convert asyncapi documents older to newer versions or OpenAPI/postman-collection documents to AsyncAPI ``` -_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/convert.ts)_ +_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/convert.ts)_ ## `asyncapi diff OLD NEW` @@ -375,7 +375,7 @@ DESCRIPTION Find diff between two asyncapi files ``` -_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/diff.ts)_ +_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/diff.ts)_ ## `asyncapi format [SPEC-FILE]` @@ -398,7 +398,7 @@ DESCRIPTION Convert asyncapi documents from any format to yaml, yml or JSON ``` -_See code: [src/commands/format.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/format.ts)_ +_See code: [src/commands/format.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/format.ts)_ ## `asyncapi generate` @@ -412,7 +412,7 @@ DESCRIPTION Generate typed models or other things like clients, applications or docs using AsyncAPI Generator templates. ``` -_See code: [src/commands/generate/index.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/generate/index.ts)_ +_See code: [src/commands/generate/index.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/generate/index.ts)_ ## `asyncapi generate fromTemplate ASYNCAPI TEMPLATE` @@ -457,7 +457,7 @@ EXAMPLES $ asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template --param version=1.0.0 singleFile=true --output ./docs --force-write ``` -_See code: [src/commands/generate/fromTemplate.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/generate/fromTemplate.ts)_ +_See code: [src/commands/generate/fromTemplate.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/generate/fromTemplate.ts)_ ## `asyncapi generate models LANGUAGE FILE` @@ -527,7 +527,7 @@ DESCRIPTION Generates typed models ``` -_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/generate/models.ts)_ +_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/generate/models.ts)_ ## `asyncapi new` @@ -585,7 +585,7 @@ EXAMPLES $ asyncapi new --file-name=my-asyncapi.yml --example=default-example.yml --no-tty - create a new file with a specific name, using one of the examples and without interactive mode ``` -_See code: [src/commands/new/index.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/new/index.ts)_ +_See code: [src/commands/new/index.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/new/index.ts)_ ## `asyncapi new file` @@ -643,7 +643,7 @@ EXAMPLES $ asyncapi new --file-name=my-asyncapi.yml --example=default-example.yml --no-tty - create a new file with a specific name, using one of the examples and without interactive mode ``` -_See code: [src/commands/new/file.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/new/file.ts)_ +_See code: [src/commands/new/file.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/new/file.ts)_ ## `asyncapi new glee` @@ -665,7 +665,7 @@ DESCRIPTION Creates a new Glee project ``` -_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/new/glee.ts)_ +_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/new/glee.ts)_ ## `asyncapi new template` @@ -689,7 +689,7 @@ DESCRIPTION Creates a new template ``` -_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/new/template.ts)_ +_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/new/template.ts)_ ## `asyncapi optimize [SPEC-FILE]` @@ -731,7 +731,7 @@ EXAMPLES $ asyncapi optimize ./asyncapi.yaml --ignore=schema ``` -_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/optimize.ts)_ +_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/optimize.ts)_ ## `asyncapi pretty SPEC-FILE` @@ -756,7 +756,7 @@ EXAMPLES $ asyncapi pretty ./asyncapi.yaml --output formatted-asyncapi.yaml ``` -_See code: [src/commands/pretty.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/pretty.ts)_ +_See code: [src/commands/pretty.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/pretty.ts)_ ## `asyncapi start` @@ -770,7 +770,7 @@ DESCRIPTION Starts AsyncAPI-related services. Currently, it supports launching the AsyncAPI Studio ``` -_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/start/index.ts)_ +_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/start/index.ts)_ ## `asyncapi start studio` @@ -789,7 +789,7 @@ DESCRIPTION starts a new local instance of Studio ``` -_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/start/studio.ts)_ +_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/start/studio.ts)_ ## `asyncapi validate [SPEC-FILE]` @@ -820,5 +820,5 @@ DESCRIPTION validate asyncapi file ``` -_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.11.0/src/commands/validate.ts)_ +_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.11.1/src/commands/validate.ts)_ From 9976010d3282c42ae1be8666b9dcd297f228b36c Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela <90895835+vishvamsinh28@users.noreply.github.com> Date: Sat, 7 Dec 2024 22:32:27 +0530 Subject: [PATCH 4/4] feat: add tests for tool-object script (#3265) Co-authored-by: asyncapi-bot --- scripts/tools/tools-object.js | 113 +++++++++++----------- tests/helper/toolsObjectData.js | 79 +++++++++++++++ tests/tools/tools-object.test.js | 161 +++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+), 55 deletions(-) create mode 100644 tests/helper/toolsObjectData.js create mode 100644 tests/tools/tools-object.test.js diff --git a/scripts/tools/tools-object.js b/scripts/tools/tools-object.js index e5a30334f7d3..1d8c73f8074b 100644 --- a/scripts/tools/tools-object.js +++ b/scripts/tools/tools-object.js @@ -9,7 +9,6 @@ addFormats(ajv, ["uri"]) const validate = ajv.compile(schema) const { convertToJson } = require('../utils'); - // Config options set for the Fuse object const options = { includeScore: true, @@ -25,8 +24,8 @@ const fuse = new Fuse(categoryList, options) // isAsyncAPIrepo boolean variable to define whether the tool repository is under // AsyncAPI organization or not, to create a JSON tool object as required in the frontend // side to show ToolCard. -const createToolObject = async (toolFile, repositoryUrl='', repoDescription='', isAsyncAPIrepo='') => { - let resultantObject = { +const createToolObject = async (toolFile, repositoryUrl = '', repoDescription = '', isAsyncAPIrepo = '') => { + const resultantObject = { title: toolFile.title, description: toolFile?.description ? toolFile.description : repoDescription, links: { @@ -47,67 +46,71 @@ const createToolObject = async (toolFile, repositoryUrl='', repoDescription='', // and creating a JSON tool object in which all the tools are listed in defined // categories order, which is then updated in `automated-tools.json` file. async function convertTools(data) { - let finalToolsObject = {}; - const dataArray = data.items; - - // initialising finalToolsObject with all categories inside it with proper elements in each category - for (var index in categoryList) { - finalToolsObject[categoryList[index].name] = { - description: categoryList[index].description, - toolsList: [] - }; - } + try { + let finalToolsObject = {}; + const dataArray = data.items; - for (let tool of dataArray) { - try { - if (tool.name.startsWith('.asyncapi-tool')) { - // extracting the reference id of the repository which will be used to extract the path of the .asyncapi-tool file in the Tools repository - // ex: for a url = "https://api.github.com/repositories/351453552/contents/.asyncapi-tool?ref=61855e7365a881e98c2fe667a658a0005753d873" - // the text (id) present after '=' gives us a reference id for the repo - let reference_id = tool.url.split("=")[1]; - let download_url = `https://raw.githubusercontent.com/${tool.repository.full_name}/${reference_id}/${tool.path}`; + // initialising finalToolsObject with all categories inside it with proper elements in each category + finalToolsObject = Object.fromEntries( + categoryList.map((category) => [ + category.name, + { + description: category.description, + toolsList: [] + } + ]) + ); - const { data: toolFileContent } = await axios.get(download_url); + await Promise.all(dataArray.map(async (tool) => { + try { + if (tool.name.startsWith('.asyncapi-tool')) { + const referenceId = tool.url.split('=')[1]; + const downloadUrl = `https://raw.githubusercontent.com/${tool.repository.full_name}/${referenceId}/${tool.path}`; - //some stuff can be YAML - const jsonToolFileContent = await convertToJson(toolFileContent) + const { data: toolFileContent } = await axios.get(downloadUrl); - //validating against JSON Schema for tools file - const isValid = await validate(jsonToolFileContent) + //some stuff can be YAML + const jsonToolFileContent = await convertToJson(toolFileContent) - if (isValid) { - let repositoryUrl = tool.repository.html_url; - let repoDescription = tool.repository.description; - let isAsyncAPIrepo = tool.repository.owner.login === "asyncapi"; - let toolObject = await createToolObject(jsonToolFileContent, repositoryUrl, repoDescription, isAsyncAPIrepo); + //validating against JSON Schema for tools file + const isValid = await validate(jsonToolFileContent) - // Tool Object is appended to each category array according to Fuse search for categories inside Tool Object - jsonToolFileContent.filters.categories.forEach(async (category) => { - const categorySearch = await fuse.search(category); + if (isValid) { + const repositoryUrl = tool.repository.html_url; + const repoDescription = tool.repository.description; + const isAsyncAPIrepo = tool.repository.owner.login === 'asyncapi'; + const toolObject = await createToolObject( + jsonToolFileContent, + repositoryUrl, + repoDescription, + isAsyncAPIrepo + ); - if (categorySearch.length) { - let searchedCategoryName = categorySearch[0].item.name - if (!finalToolsObject[searchedCategoryName].toolsList.find((element => element === toolObject))) - finalToolsObject[searchedCategoryName].toolsList.push(toolObject); - } else { - // if Tool object has a category, not defined in our categorylist, then this provides a `other` category to the tool. - if (!finalToolsObject['Others'].toolsList.find((element => element === toolObject))) - finalToolsObject['Others'].toolsList.push(toolObject); - } - }); - } else { - console.error('Script is not failing, it is just dropping errors for further investigation'); - console.error('Invalid .asyncapi-tool file.'); - console.error(`Located in: ${tool.html_url}`); - console.error('Validation errors:', JSON.stringify(validate.errors, null, 2)); + // Tool Object is appended to each category array according to Fuse search for categories inside Tool Object + await Promise.all(jsonToolFileContent.filters.categories.map(async (category) => { + const categorySearch = await fuse.search(category); + const targetCategory = categorySearch.length ? categorySearch[0].item.name : 'Others'; + const { toolsList } = finalToolsObject[targetCategory]; + if (!toolsList.includes(toolObject)) { + toolsList.push(toolObject); + } + })); + } else { + console.error('Script is not failing, it is just dropping errors for further investigation'); + console.error('Invalid .asyncapi-tool file.'); + console.error(`Located in: ${tool.html_url}`); + console.error('Validation errors:', JSON.stringify(validate.errors, null, 2)); + } } + } catch (err) { + console.error(err) + throw err; } - } catch (err) { - console.error(err) - throw err; - } + })) + return finalToolsObject; + } catch (err) { + throw new Error(`Error processing tool: ${err.message}`) } - return finalToolsObject; } -module.exports = {convertTools, createToolObject} \ No newline at end of file +module.exports = { convertTools, createToolObject } diff --git a/tests/helper/toolsObjectData.js b/tests/helper/toolsObjectData.js new file mode 100644 index 000000000000..929d985f9773 --- /dev/null +++ b/tests/helper/toolsObjectData.js @@ -0,0 +1,79 @@ +const createToolRepositoryData = ({ + name = '.asyncapi-tool', + refId = '61855e7365a881e98c2fe667a658a0005753d873', + owner = 'asyncapi', + repoName = 'example-repo', + description = 'Example repository', + path = '.asyncapi-tool' +} = {}) => ({ + name, + url: `https://api.github.com/repositories/351453552/contents/${path}?ref=${refId}`, + repository: { + full_name: `${owner}/${repoName}`, + html_url: `https://github.com/${owner}/${repoName}`, + description, + owner: { login: owner } + }, + path +}); + +const createToolFileContent = ({ + title = 'Example Tool', + description = 'This is an example tool.', + repoUrl = null, + categories = ['Category1'], + hasCommercial = false, + additionalLinks = {}, + additionalFilters = {} +} = {}) => ({ + title, + description, + links: { + repoUrl: repoUrl || `https://github.com/asyncapi/${encodeURIComponent(title.toLowerCase().replace(/\s+/g, '-'))}`, + ...additionalLinks + }, + filters: { categories, hasCommercial, ...additionalFilters } +}); + +const createExpectedToolObject = ({ + title = 'Example Tool', + description = 'This is an example tool.', + repoUrl = null, + categories = ['Category1'], + hasCommercial = false, + isAsyncAPIOwner = true, + additionalLinks = {}, + additionalFilters = {} +} = {}) => + createToolFileContent({ + title, + description, + repoUrl, + categories, + hasCommercial, + additionalLinks, + additionalFilters: { isAsyncAPIOwner, ...additionalFilters } + }); + +const createMockData = (tools = []) => ({ + items: tools.map((tool) => + typeof tool === 'string' + ? createToolRepositoryData({ name: `.asyncapi-tool-${tool}`, repoName: tool }) + : createToolRepositoryData(tool) + ) +}); + +const createMalformedYAML = ({ + title = 'Malformed Tool', + description = 'This tool has malformed YAML.', + repoUrl = 'https://github.com/asyncapi/malformed-repo' } = {}) => ` + title: ${title} + description: ${description} + links: + repoUrl: ${repoUrl} + filters: + categories: + - Category1 +`; + +module.exports = { createToolFileContent, createExpectedToolObject, createMockData, createMalformedYAML }; diff --git a/tests/tools/tools-object.test.js b/tests/tools/tools-object.test.js new file mode 100644 index 000000000000..2577a2a27d94 --- /dev/null +++ b/tests/tools/tools-object.test.js @@ -0,0 +1,161 @@ +const axios = require('axios'); +const { convertTools, createToolObject } = require('../../scripts/tools/tools-object'); +const { + createToolFileContent, + createExpectedToolObject, + createMockData, + createMalformedYAML +} = require('../helper/toolsObjectData'); + +jest.mock('axios'); +jest.mock('../../scripts/tools/categorylist', () => ({ + categoryList: [ + { name: 'Category1', tag: 'Category1', description: 'Description for Category1' }, + { name: 'Others', tag: 'Others', description: 'Other tools category' }, + ] +})); + +describe('Tools Object', () => { + beforeEach(() => { + axios.get.mockClear(); + console.error = jest.fn(); + }); + + const mockToolData = (toolContent, toolNames = ['valid-tool']) => { + const mockData = createMockData(toolNames.map((name) => ({ name: `.asyncapi-tool-${name}`, repoName: name }))); + axios.get.mockResolvedValue({ data: toolContent }); + return mockData; + }; + + it('should create a tool object with provided parameters', async () => { + const toolFile = createToolFileContent({ + title: 'Test Tool', + description: 'Test Description', + hasCommercial: true, + additionalLinks: { docsUrl: 'https://docs.example.com' } + }); + + const expected = createExpectedToolObject({ + title: 'Test Tool', + description: 'Test Description', + hasCommercial: true, + additionalLinks: { docsUrl: 'https://docs.example.com' } + }); + + const result = await createToolObject( + toolFile, + expected.links.repoUrl, + 'Repository Description', + true + ); + + expect(result).toEqual(expected); + }); + + it('should convert tools data correctly', async () => { + const toolContent = createToolFileContent({ title: 'Valid Tool', categories: ['Category1'] }); + const mockData = mockToolData(toolContent); + + const result = await convertTools(mockData); + + expect(result.Category1.toolsList).toHaveLength(1); + expect(result.Category1.toolsList[0].title).toBe('Valid Tool'); + }); + + it('should assign tool to Others category if no matching category is found', async () => { + const toolContent = createToolFileContent({ title: 'Unknown Category Tool', categories: ['UnknownCategory'] }); + const mockData = mockToolData(toolContent); + + const result = await convertTools(mockData); + + expect(result.Others.toolsList).toHaveLength(1); + expect(result.Others.toolsList[0].title).toBe('Unknown Category Tool'); + }); + + it('should log errors for invalid .asyncapi-tool file', async () => { + const invalidContent = createToolFileContent({ + title: 'Invalid Tool', + additionalFilters: { invalidField: true } + }); + const mockData = mockToolData(invalidContent); + + await convertTools(mockData); + + expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Script is not failing')); + expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Invalid .asyncapi-tool file')); + }); + + it('should add duplicate tool objects to the same category', async () => { + const toolContent = createToolFileContent({ + title: 'Duplicate Tool', + categories: ['Category1'] + }); + + const mockData = createMockData([ + { name: '.asyncapi-tool-dup1', repoName: 'dup1' }, + { name: '.asyncapi-tool-dup2', repoName: 'dup2' } + ]); + + axios.get.mockResolvedValue({ data: toolContent }); + + const result = await convertTools(mockData); + + expect(result.Category1.toolsList).toHaveLength(2); + expect(result.Category1.toolsList[0].title).toBe('Duplicate Tool'); + expect(result.Category1.toolsList[1].title).toBe('Duplicate Tool'); + }); + + it('should add tool to Others category only once', async () => { + const toolContent = createToolFileContent({ + title: 'Duplicate Tool in Others', + categories: ['UnknownCategory'] + }); + + const mockData = mockToolData(toolContent); + + const result = await convertTools(mockData); + + expect(result.Others.toolsList).toHaveLength(1); + expect(result.Others.toolsList[0].title).toBe('Duplicate Tool in Others'); + }); + + it('should throw an error if axios.get fails', async () => { + const mockData = createMockData([{ + name: '.asyncapi-tool-error', + repoName: 'error-tool' + }]); + + axios.get.mockRejectedValue(new Error('Network Error')); + + await expect(convertTools(mockData)).rejects.toThrow('Network Error'); + }); + + it('should handle malformed JSON in tool file', async () => { + const malformedContent = createMalformedYAML(); + await expect(convertTools(malformedContent)).rejects.toThrow(); + }); + + it('should use repository description when tool description is missing', async () => { + const toolFile = createToolFileContent({ + title: 'No Description Tool', + description: '', + }); + + const repositoryDescription = 'Fallback Repository Description'; + const mockData = createMockData([{ + name: '.asyncapi-tool-no-description', + repoName: 'no-description', + description: repositoryDescription + }]); + + axios.get.mockResolvedValue({ data: toolFile }); + + const result = await convertTools(mockData); + + const toolObject = result.Category1.toolsList[0]; + + expect(toolObject.description).toBe(repositoryDescription); + expect(toolObject.title).toBe('No Description Tool'); + }); + +});