From a2b2b4b8df13e0771e7decad4b00a42dc6ca1a55 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Mon, 22 Jul 2024 14:52:46 +0200 Subject: [PATCH 01/69] docs(community): update latest maintainers list (#3107) --- config/MAINTAINERS.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/MAINTAINERS.json b/config/MAINTAINERS.json index 281ca5fc7d5e..823bc17b8a1c 100644 --- a/config/MAINTAINERS.json +++ b/config/MAINTAINERS.json @@ -84,7 +84,7 @@ }, { "name": "Cameron Rushton", - "github": "cameronrushton", + "github": "CameronRushton", "slack": "U01DVKKAV5K", "availableForHire": false, "company": "Solace", From d52eecaaada127db8a3ce29ae96521d4b57e375e Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Tue, 23 Jul 2024 09:02:49 +0200 Subject: [PATCH 02/69] docs(community): update latest maintainers list (#3110) --- config/MAINTAINERS.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/config/MAINTAINERS.json b/config/MAINTAINERS.json index 823bc17b8a1c..74660bc61a0a 100644 --- a/config/MAINTAINERS.json +++ b/config/MAINTAINERS.json @@ -143,7 +143,7 @@ }, { "name": "Gerald Loeffler", - "github": "geraldloeffler", + "github": "GeraldLoeffler", "linkedin": "geraldloeffler", "slack": "U01P5QDLP0X", "availableForHire": false, @@ -173,7 +173,7 @@ }, { "name": "Khuda Dad Nomani", - "github": "khudadad414", + "github": "KhudaDad414", "twitter": "KhudaDadNomani", "linkedin": "khudadadnomani", "slack": "U01RVRD1TCL", @@ -262,7 +262,7 @@ { "name": "Azeez Elegbede", "linkedin": "acebuild", - "github": "acethecreator", + "github": "AceTheCreator", "twitter": "_acebuild", "slack": "U01RWDD69PZ", "company": "Postman", @@ -299,7 +299,7 @@ }, { "name": "Nektarios Fifes", - "github": "nektariosfifes", + "github": "NektariosFifes", "linkedin": "nektarios-fifes-372740220", "slack": "U01SE93Q48N", "availableForHire": true, @@ -310,7 +310,7 @@ }, { "name": "Pavel Bodiachevskii", - "github": "pakisan", + "github": "Pakisan", "slack": "U0132LQU8C9", "twitter": "pbodiachevskii", "availableForHire": false, @@ -394,7 +394,7 @@ }, { "name": "Souvik De", - "github": "souvikns", + "github": "Souvikns", "slack": "U01SGCZMJKW", "twitter": "souvik_ns", "linkedin": "souvik-de-a2b941169", @@ -424,7 +424,7 @@ }, { "name": "David Pereira", - "github": "bolt04", + "github": "BOLT04", "twitter": "BOLT2938", "slack": "U02EC8BT0TX", "linkedin": "jos\u00e9-david-pereira-13ba5315a", @@ -448,7 +448,7 @@ }, { "name": "Kieran Murphy", - "github": "kieranm1999", + "github": "KieranM1999", "linkedin": "kieran-murphy-175b0412b", "availableForHire": false, "slack": "U02FT2TKM37", @@ -460,7 +460,7 @@ }, { "name": "Tom Jefferson", - "github": "jefflufc", + "github": "JEFFLUFC", "linkedin": "t-jefferson", "slack": "U02FPPCEH6H", "availableForHire": false, @@ -483,7 +483,7 @@ }, { "name": "Semen Tenishchev", - "github": "tenischev", + "github": "Tenischev", "linkedin": "semen-tenishchev", "availableForHire": true, "slack": "U011D1DAU6S", @@ -494,7 +494,7 @@ }, { "name": "Samridhi Agrawal", - "github": "samridhi-98", + "github": "Samridhi-98", "slack": "U02T2MY9W5T", "linkedin": "samridhi-agrawal-1713201ab", "availableForHire": false, @@ -531,7 +531,7 @@ }, { "name": "Florence Njeri", - "github": "florence-njeri", + "github": "Florence-Njeri", "linkedin": "florencenjeri", "slack": "U03D18YKX2M", "twitter": "njericodes", @@ -571,7 +571,7 @@ }, { "name": "Alexander Wichmann", - "github": "visualbean", + "github": "VisualBean", "linkedin": "alexcarlsen", "slack": "U04C58GB8TF", "availableForHire": false, @@ -595,7 +595,7 @@ }, { "name": "Heiko Henning", - "github": "greenrover", + "github": "GreenRover", "slack": "U03AC4G51H8", "availableForHire": false, "company": "mtrail GmbH", From 2fe6689d8abfa0855bb63ded6412891b9a505d9e Mon Sep 17 00:00:00 2001 From: V Thulisile Sibanda <66913810+thulieblack@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:12:05 +0200 Subject: [PATCH 03/69] feat: update banner for paris conference (#3098) Co-authored-by: asyncapi-bot --- components/campaigns/banners.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/campaigns/banners.ts b/components/campaigns/banners.ts index 457eaa5226fc..11c2180f8abe 100644 --- a/components/campaigns/banners.ts +++ b/components/campaigns/banners.ts @@ -15,18 +15,18 @@ function shouldShowBanner(cfpDeadline: string) { return true; } -const cfpDeadlineLondon = '2024-07-12T06:00:00Z'; -const showBannerLondon = shouldShowBanner(cfpDeadlineLondon); +const cfpDeadlineParis = '2024-09-20T06:00:00Z'; +const showBannerParis = shouldShowBanner(cfpDeadlineParis); export const banners = [ { title: "AsyncAPI Conf on Tour'24", - city: 'London', - dateLocation: '18 - 19 of September, 2024 | United Kingdom, London', + city: 'Paris', + dateLocation: '3rd - 5th of December, 2024 | France, Paris', cfaText: 'Apply To Speak', eventName: 'the end of Call for Speakers', - cfpDeadline: cfpDeadlineLondon, - link: 'https://conference.asyncapi.com/venue/London', - show: showBannerLondon + cfpDeadline: cfpDeadlineParis, + link: 'https://conference.asyncapi.com/venue/Paris', + show: showBannerParis } ]; From 2d0669d2ffa261442787485eebc5637086b715d1 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon <67381+philsturgeon@users.noreply.github.com> Date: Wed, 24 Jul 2024 05:40:49 +0100 Subject: [PATCH 04/69] docs: update bump.sh tool card (#3065) Co-authored-by: Lukasz Gornicki --- config/tools-manual.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/tools-manual.json b/config/tools-manual.json index 3eec3dff9532..902dcd3b8f65 100644 --- a/config/tools-manual.json +++ b/config/tools-manual.json @@ -341,7 +341,8 @@ "title": "Bump.sh", "description": "OpenAPI 2 & 3 / AsyncAPI 2 documentation generator, with automatic changelog and visual diff.", "links": { - "websiteUrl": "https://bump.sh/" + "websiteUrl": "https://bump.sh/", + "docsUrl": "https://docs.bump.sh/help/" }, "filters": { "categories": ["documentation-generator"], From 840e3a8374e0006f9920956990187b24610474b5 Mon Sep 17 00:00:00 2001 From: TenzDelek <122612557+TenzDelek@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:28:58 +0530 Subject: [PATCH 05/69] fix: front page alignment (#3112) --- components/campaigns/AnnouncementHero.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/campaigns/AnnouncementHero.tsx b/components/campaigns/AnnouncementHero.tsx index 3d924e97afa9..ac85509f5d51 100644 --- a/components/campaigns/AnnouncementHero.tsx +++ b/components/campaigns/AnnouncementHero.tsx @@ -56,8 +56,8 @@ export default function AnnouncementHero({ className = '', small = false }: IAnn )} -
-
+
+
{banners.map( (banner, index) => banner.show && ( From 1ab6e4ab89fab27c03602783e19cec2235e28dc6 Mon Sep 17 00:00:00 2001 From: Akshit Gupta <96991785+akkshitgupta@users.noreply.github.com> Date: Wed, 24 Jul 2024 21:48:27 +0530 Subject: [PATCH 06/69] feat: add rendering of case study asyncapi document (#2770) Co-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com>%0ACo-authored-by: Lukasz Gornicki --- components/helpers/read-yaml-file.js | 12 ++++++++++++ components/helpers/read-yaml-file.ts | 12 ++++++++++++ pages/casestudies/[id].tsx | 6 ++++++ utils/staticHelpers.ts | 5 +++++ 4 files changed, 35 insertions(+) create mode 100644 components/helpers/read-yaml-file.js create mode 100644 components/helpers/read-yaml-file.ts diff --git a/components/helpers/read-yaml-file.js b/components/helpers/read-yaml-file.js new file mode 100644 index 000000000000..8cfbe5217463 --- /dev/null +++ b/components/helpers/read-yaml-file.js @@ -0,0 +1,12 @@ +import fs from 'fs/promises'; + +export const readYamlFile = async (fileName) => { + try { + const data = await fs.readFile(`./public/${fileName}`, 'utf-8'); + const yamlString = `\`\`\`yaml\n${data}\`\`\``; + + return yamlString; + } catch (error) { + throw new Error(`Error: something went wrong while reading ${fileName} file: ${error.message}`); + } +}; diff --git a/components/helpers/read-yaml-file.ts b/components/helpers/read-yaml-file.ts new file mode 100644 index 000000000000..f8c1e361b1e5 --- /dev/null +++ b/components/helpers/read-yaml-file.ts @@ -0,0 +1,12 @@ +import fs from 'fs/promises'; + +export const readYamlFile = async (fileName: string) => { + try { + const data = await fs.readFile(`./public/${fileName}`, 'utf-8'); + const yamlString = `\`\`\`yaml\n${data}\`\`\``; + + return yamlString; + } catch (error: any) { + throw new Error(`Error: something went wrong while reading ${fileName} file: ${error.message}`); + } +}; diff --git a/pages/casestudies/[id].tsx b/pages/casestudies/[id].tsx index 4c0f781806c4..d0668740edb9 100644 --- a/pages/casestudies/[id].tsx +++ b/pages/casestudies/[id].tsx @@ -14,6 +14,7 @@ import Heading from '../../components/typography/Heading'; import Paragraph from '../../components/typography/Paragraph'; import CaseStudiesList from '../../config/case-studies.json'; import { generateCaseStudyContent } from '../../utils/staticHelpers'; +import { readYamlFile } from '@/components/helpers/read-yaml-file'; interface IndexProps { casestudy: ICaseStudy; @@ -34,6 +35,7 @@ interface IndexProps { asyncapiBindings: MDXRemoteSerializeResult; asyncapiTools: MDXRemoteSerializeResult; additionalResources: MDXRemoteSerializeResult; + fullExample: MDXRemoteSerializeResult; } const renderContent = ( @@ -96,6 +98,7 @@ const renderContent = ( */ export async function getStaticProps({ params }: { params: { id: string } }) { const data = CaseStudiesList.filter((p: { id: string }) => p.id === params.id); + const asyncApiDoc = await readYamlFile(data[0].asyncapi.fullExample); return { props: { @@ -116,6 +119,7 @@ export async function getStaticProps({ params }: { params: { id: string } }) { asyncapiDocumentation: await serialize(data[0].asyncapi.documentation), asyncapiBindings: await serialize(data[0].asyncapi.bindings), asyncapiTools: await serialize(data[0].asyncapi.tools), + fullExample: await serialize(asyncApiDoc), additionalResources: await serialize(data[0].additionalResources) } }; @@ -153,6 +157,7 @@ const Index: React.FC = ({ asyncapiDocumentation, asyncapiBindings, asyncapiTools, + fullExample, additionalResources }) => { const image = '/img/social/website-card.png'; @@ -177,6 +182,7 @@ const Index: React.FC = ({ asyncapiBindings, asyncapiTools, additionalResources, + fullExample, casestudy }); diff --git a/utils/staticHelpers.ts b/utils/staticHelpers.ts index 230f26da6ee0..e8280fa88d41 100644 --- a/utils/staticHelpers.ts +++ b/utils/staticHelpers.ts @@ -64,6 +64,7 @@ export const generateCaseStudyContent = (data: any) => { asyncapiBindings, asyncapiTools, additionalResources, + fullExample, casestudy } = data; const { languages } = casestudy.technical; @@ -166,6 +167,10 @@ export const generateCaseStudyContent = (data: any) => { ] } ] + }, + { + title: "Production-use AsyncAPI document", + content: fullExample, } ]; }; From 63d64bfb5d12a6511a881f866e643414414d86ca Mon Sep 17 00:00:00 2001 From: TenzDelek <122612557+TenzDelek@users.noreply.github.com> Date: Fri, 26 Jul 2024 19:29:01 +0530 Subject: [PATCH 07/69] fix: blog landing page having problem with showing 2 authors names (#3051) Co-authored-by: asyncapi-bot %0ACo-authored-by: Lukasz Gornicki --- components/campaigns/AnnouncementHero.tsx | 2 +- components/helpers/read-yaml-file.js | 12 ------------ components/navigation/BlogPostItem.tsx | 6 +++++- pages/casestudies/[id].tsx | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 components/helpers/read-yaml-file.js diff --git a/components/campaigns/AnnouncementHero.tsx b/components/campaigns/AnnouncementHero.tsx index ac85509f5d51..04ffae7f2de7 100644 --- a/components/campaigns/AnnouncementHero.tsx +++ b/components/campaigns/AnnouncementHero.tsx @@ -57,7 +57,7 @@ export default function AnnouncementHero({ className = '', small = false }: IAnn
)}
-
+
{banners.map( (banner, index) => banner.show && ( diff --git a/components/helpers/read-yaml-file.js b/components/helpers/read-yaml-file.js deleted file mode 100644 index 8cfbe5217463..000000000000 --- a/components/helpers/read-yaml-file.js +++ /dev/null @@ -1,12 +0,0 @@ -import fs from 'fs/promises'; - -export const readYamlFile = async (fileName) => { - try { - const data = await fs.readFile(`./public/${fileName}`, 'utf-8'); - const yamlString = `\`\`\`yaml\n${data}\`\`\``; - - return yamlString; - } catch (error) { - throw new Error(`Error: something went wrong while reading ${fileName} file: ${error.message}`); - } -}; diff --git a/components/navigation/BlogPostItem.tsx b/components/navigation/BlogPostItem.tsx index 13c2f6fe29dc..9a2fb20076a1 100644 --- a/components/navigation/BlogPostItem.tsx +++ b/components/navigation/BlogPostItem.tsx @@ -112,7 +112,11 @@ export default forwardRef(function BlogPostItem( author.name ) ) - .reduce((prev, curr) => [prev, ' & ', curr].join(''))} + .reduce((prev, curr) => ( + <> + {prev} & {curr} + + ))} diff --git a/pages/casestudies/[id].tsx b/pages/casestudies/[id].tsx index d0668740edb9..d418e038629d 100644 --- a/pages/casestudies/[id].tsx +++ b/pages/casestudies/[id].tsx @@ -3,6 +3,7 @@ import type { MDXRemoteSerializeResult } from 'next-mdx-remote'; import { MDXRemote } from 'next-mdx-remote'; import { serialize } from 'next-mdx-remote/serialize'; +import { readYamlFile } from '@/components/helpers/read-yaml-file'; import type { ICaseStudy } from '@/types/post'; import { HeadingTypeStyle } from '@/types/typography/Heading'; import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; @@ -14,7 +15,6 @@ import Heading from '../../components/typography/Heading'; import Paragraph from '../../components/typography/Paragraph'; import CaseStudiesList from '../../config/case-studies.json'; import { generateCaseStudyContent } from '../../utils/staticHelpers'; -import { readYamlFile } from '@/components/helpers/read-yaml-file'; interface IndexProps { casestudy: ICaseStudy; From 615930638c2610a358616650c821ef560d602790 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Mon, 29 Jul 2024 02:34:13 +0200 Subject: [PATCH 08/69] chore: update tools.json (#3124) --- config/all-tags.json | 2 +- config/tools-automated.json | 196 ++++++++++++++++++------------------ config/tools.json | 2 +- 3 files changed, 100 insertions(+), 100 deletions(-) diff --git a/config/all-tags.json b/config/all-tags.json index 9f22bcd87c86..bcbe78efc37c 100644 --- a/config/all-tags.json +++ b/config/all-tags.json @@ -1 +1 @@ -{"languages":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"},{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"},{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"},{"name":"HTML","color":"bg-[#E2A291]","borderColor":"border-[#E44D26]"},{"name":"C/C++","color":"bg-[#93CDEF]","borderColor":"border-[#0080CC]"},{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"},{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"},{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"},{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"},{"name":"Scala","color":"bg-[#FFA299]","borderColor":"border-[#DF301F]"},{"name":"Markdown","color":"bg-[#BABEBF]","borderColor":"border-[#445B64]"},{"name":"YAML","color":"bg-[#FFB764]","borderColor":"border-[#F1901F]"},{"name":"R","color":"bg-[#84B5ED]","borderColor":"border-[#246BBE]"},{"name":"Ruby","color":"bg-[#FF8289]","borderColor":"border-[#FF000F]"},{"name":"Rust","color":"bg-[#FFB8AA]","borderColor":"border-[#E43716]"},{"name":"Shell","color":"bg-[#87D4FF]","borderColor":"border-[#389ED7]"},{"name":"Groovy","color":"bg-[#B6D5E5]","borderColor":"border-[#609DBC]"}],"technologies":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Hermes","color":"bg-[#8AEEBD]","borderColor":"border-[#2AB672]"},{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"},{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"AWS","color":"bg-[#FF9F59]","borderColor":"border-[#EF6703]"},{"name":"Docker","color":"bg-[#B8E0FF]","borderColor":"border-[#2596ED]"},{"name":"Node-RED","color":"bg-[#FF7474]","borderColor":"border-[#8F0101]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Saas","color":"bg-[#6AB8EC]","borderColor":"border-[#2275AD]"},{"name":"Kubernetes-native","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Scala","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Azure","color":"bg-[#4B93FF]","borderColor":"border-[#015ADF]"},{"name":"Jenkins","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Nest Js","color":"bg-[#E1224E]","borderColor":"border-[#B9012b]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Socket.IO","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Kotlin","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Groovy","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Markdown","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Shell","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"WebComponents","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Babel","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Storybook","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"AsyncAPI Generator","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JetBrains","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"IntelliJ IDEA","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"VSCode","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"SmartPaste","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Java","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"HTML","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}]} \ No newline at end of file +{"languages":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"},{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"},{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"},{"name":"HTML","color":"bg-[#E2A291]","borderColor":"border-[#E44D26]"},{"name":"C/C++","color":"bg-[#93CDEF]","borderColor":"border-[#0080CC]"},{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"},{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"},{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"},{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"},{"name":"Scala","color":"bg-[#FFA299]","borderColor":"border-[#DF301F]"},{"name":"Markdown","color":"bg-[#BABEBF]","borderColor":"border-[#445B64]"},{"name":"YAML","color":"bg-[#FFB764]","borderColor":"border-[#F1901F]"},{"name":"R","color":"bg-[#84B5ED]","borderColor":"border-[#246BBE]"},{"name":"Ruby","color":"bg-[#FF8289]","borderColor":"border-[#FF000F]"},{"name":"Rust","color":"bg-[#FFB8AA]","borderColor":"border-[#E43716]"},{"name":"Shell","color":"bg-[#87D4FF]","borderColor":"border-[#389ED7]"},{"name":"Groovy","color":"bg-[#B6D5E5]","borderColor":"border-[#609DBC]"}],"technologies":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Hermes","color":"bg-[#8AEEBD]","borderColor":"border-[#2AB672]"},{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"},{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"AWS","color":"bg-[#FF9F59]","borderColor":"border-[#EF6703]"},{"name":"Docker","color":"bg-[#B8E0FF]","borderColor":"border-[#2596ED]"},{"name":"Node-RED","color":"bg-[#FF7474]","borderColor":"border-[#8F0101]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Saas","color":"bg-[#6AB8EC]","borderColor":"border-[#2275AD]"},{"name":"Kubernetes-native","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Scala","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Azure","color":"bg-[#4B93FF]","borderColor":"border-[#015ADF]"},{"name":"Jenkins","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Nest Js","color":"bg-[#E1224E]","borderColor":"border-[#B9012b]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Socket.IO","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Kotlin","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Groovy","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Markdown","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Shell","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"WebComponents","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Babel","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Storybook","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"AsyncAPI Generator","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"VSCode","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"SmartPaste","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JetBrains","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"IntelliJ IDEA","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Java","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"HTML","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}]} \ No newline at end of file diff --git a/config/tools-automated.json b/config/tools-automated.json index b73e3355bfe0..c4bfd858e238 100644 --- a/config/tools-automated.json +++ b/config/tools-automated.json @@ -91,44 +91,44 @@ } }, { - "title": "SIO-AsyncAPI", - "description": "This is code-first approach to generate AsyncAPI specification from Socket.IO server.", + "title": "Zod Sockets", + "description": "Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.", "links": { - "websiteUrl": "https://github.com/daler-rahimov/sio-asyncapi", - "docsUrl": "https://github.com/daler-rahimov/sio-asyncapi", - "repoUrl": "https://github.com/daler-rahimov/sio-asyncapi" + "websiteUrl": "https://www.npmjs.com/package/zod-sockets", + "repoUrl": "https://github.com/RobinTail/zod-sockets" }, "filters": { - "language": "Python", + "language": "TypeScript", "technology": [ - "Socket.IO", - "Flask" + "Node.js", + "TypeScript" ], "categories": [ "code-first", - "api" + "dsl", + "framework" ], "hasCommercial": false, "isAsyncAPIOwner": false } }, { - "title": "Zod Sockets", - "description": "Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.", + "title": "SIO-AsyncAPI", + "description": "This is code-first approach to generate AsyncAPI specification from Socket.IO server.", "links": { - "websiteUrl": "https://www.npmjs.com/package/zod-sockets", - "repoUrl": "https://github.com/RobinTail/zod-sockets" + "websiteUrl": "https://github.com/daler-rahimov/sio-asyncapi", + "docsUrl": "https://github.com/daler-rahimov/sio-asyncapi", + "repoUrl": "https://github.com/daler-rahimov/sio-asyncapi" }, "filters": { - "language": "TypeScript", + "language": "Python", "technology": [ - "Node.js", - "TypeScript" + "Socket.IO", + "Flask" ], "categories": [ "code-first", - "dsl", - "framework" + "api" ], "hasCommercial": false, "isAsyncAPIOwner": false @@ -290,48 +290,48 @@ "description": "Writing YAML by hand is no fun, and maybe you don't want a GUI, so use a Domain Specific Language to write AsyncAPI in your language of choice.", "toolsList": [ { - "title": "ZenWave SDK", - "description": "DDD and API-First for Event-Driven Microservices", + "title": "Zod Sockets", + "description": "Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.", "links": { - "websiteUrl": "https://zenwave360.github.io/", - "docsUrl": "https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/", - "repoUrl": "https://github.com/zenwave360/zenwave-sdk" + "websiteUrl": "https://www.npmjs.com/package/zod-sockets", + "repoUrl": "https://github.com/RobinTail/zod-sockets" }, "filters": { - "language": "Java", + "language": "TypeScript", "technology": [ - "Maven", - "CLI", - "Spring Cloud Streams", - "JHipster JDL" + "Node.js", + "TypeScript" ], "categories": [ - "code-generator", + "code-first", "dsl", - "mocking-and-testing", - "cli" + "framework" ], "hasCommercial": false, "isAsyncAPIOwner": false } }, { - "title": "Zod Sockets", - "description": "Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.", + "title": "ZenWave SDK", + "description": "DDD and API-First for Event-Driven Microservices", "links": { - "websiteUrl": "https://www.npmjs.com/package/zod-sockets", - "repoUrl": "https://github.com/RobinTail/zod-sockets" + "websiteUrl": "https://zenwave360.github.io/", + "docsUrl": "https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/", + "repoUrl": "https://github.com/zenwave360/zenwave-sdk" }, "filters": { - "language": "TypeScript", + "language": "Java", "technology": [ - "Node.js", - "TypeScript" + "Maven", + "CLI", + "Spring Cloud Streams", + "JHipster JDL" ], "categories": [ - "code-first", + "code-generator", "dsl", - "framework" + "mocking-and-testing", + "cli" ], "hasCommercial": false, "isAsyncAPIOwner": false @@ -547,6 +547,25 @@ "CLIs": { "description": "The following is a list of tools that you can work with in terminal or do some CI/CD automation.", "toolsList": [ + { + "title": "AsyncAPI CLI", + "description": "One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n", + "links": { + "websiteUrl": "https://www.asyncapi.com/tools/cli", + "repoUrl": "https://github.com/asyncapi/cli" + }, + "filters": { + "technology": [ + "TypeScript" + ], + "categories": [ + "others", + "cli" + ], + "hasCommercial": false, + "isAsyncAPIOwner": true + } + }, { "title": "ZenWave SDK", "description": "DDD and API-First for Event-Driven Microservices", @@ -573,25 +592,6 @@ "isAsyncAPIOwner": false } }, - { - "title": "AsyncAPI CLI", - "description": "One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n", - "links": { - "websiteUrl": "https://www.asyncapi.com/tools/cli", - "repoUrl": "https://github.com/asyncapi/cli" - }, - "filters": { - "technology": [ - "TypeScript" - ], - "categories": [ - "others", - "cli" - ], - "hasCommercial": false, - "isAsyncAPIOwner": true - } - }, { "title": "AsyncAPI CLI", "description": "One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n", @@ -640,18 +640,15 @@ "description": "The following is a list of extensions for different IDEs like VSCode, IntelliJ IDEA and others", "toolsList": [ { - "title": "jAsyncAPI - IDEA plugin", - "description": "Idea plugin for the java-asyncapi - Helps to edit and validate AsyncAPI schemas.", + "title": "asyncapi-preview", + "description": "VSCode extension that enables you to:\n - Preview documentation generated using you AsyncAPI document. It uses AsyncAPI React component under the hood,\n - Create AsyncAPI documents faster using SmartPaste functionality\n", "links": { - "websiteUrl": "https://plugins.jetbrains.com/plugin/15673-asyncapi", - "docsUrl": "https://github.com/asyncapi/jasyncapi-idea-plugin#usage", - "repoUrl": "https://github.com/asyncapi/jasyncapi-idea-plugin" + "repoUrl": "https://github.com/asyncapi/vs-asyncapi-preview" }, "filters": { - "language": "Kotlin", "technology": [ - "JetBrains", - "IntelliJ IDEA" + "VSCode", + "SmartPaste" ], "categories": [ "ide-extension" @@ -661,15 +658,18 @@ } }, { - "title": "asyncapi-preview", - "description": "VSCode extension that enables you to:\n - Preview documentation generated using you AsyncAPI document. It uses AsyncAPI React component under the hood,\n - Create AsyncAPI documents faster using SmartPaste functionality\n", + "title": "jAsyncAPI - IDEA plugin", + "description": "Idea plugin for the java-asyncapi - Helps to edit and validate AsyncAPI schemas.", "links": { - "repoUrl": "https://github.com/asyncapi/vs-asyncapi-preview" + "websiteUrl": "https://plugins.jetbrains.com/plugin/15673-asyncapi", + "docsUrl": "https://github.com/asyncapi/jasyncapi-idea-plugin#usage", + "repoUrl": "https://github.com/asyncapi/jasyncapi-idea-plugin" }, "filters": { + "language": "Kotlin", "technology": [ - "VSCode", - "SmartPaste" + "JetBrains", + "IntelliJ IDEA" ], "categories": [ "ide-extension" @@ -702,19 +702,17 @@ "description": "The following is a list of templates compatible with AsyncAPI Generator. You can use them to generate apps, clients or documentation from your AsyncAPI documents.", "toolsList": [ { - "title": "Java Spring Template", - "description": "Java Spring template for the AsyncAPI Generator", + "title": "Java Template", + "description": "Java template for the AsyncAPI Generator", "links": { - "repoUrl": "https://github.com/asyncapi/java-spring-template" + "repoUrl": "https://github.com/asyncapi/java-template" }, "filters": { "language": [ "javascript" ], "technology": [ - "Springboot", - "Maven", - "Gradle" + "Java" ], "categories": [ "generator-template" @@ -724,15 +722,15 @@ } }, { - "title": "Node.js Multiprotocol Template", - "description": "This template generates a server using your AsyncAPI document. It supports multiple different protocols, like Kafka or MQTT. It is designed in the way that generated code is a library and with it's API you can start the server, send messages or register a middleware for listening incoming messages. Runtime message validation included.", + "title": "HTML Template", + "description": "HTML template for AsyncAPI Generator. Use it to generate a static docs. It is using AsyncAPI React component under the hood.", "links": { - "repoUrl": "https://github.com/asyncapi/nodejs-template" + "repoUrl": "https://github.com/asyncapi/html-template" }, "filters": { "language": "javascript", "technology": [ - "Node.js" + "HTML" ], "categories": [ "generator-template" @@ -742,15 +740,19 @@ } }, { - "title": "Node.js Websockets Template", - "description": "Node.js WebSockets template for the AsyncAPI Generator. It showcases how from a single AsyncAPI document you can generate a server and a client at the same time.", + "title": "Java Spring Template", + "description": "Java Spring template for the AsyncAPI Generator", "links": { - "repoUrl": "https://github.com/asyncapi/nodejs-ws-template" + "repoUrl": "https://github.com/asyncapi/java-spring-template" }, "filters": { - "language": "javascript", + "language": [ + "javascript" + ], "technology": [ - "Node.js" + "Springboot", + "Maven", + "Gradle" ], "categories": [ "generator-template" @@ -760,17 +762,15 @@ } }, { - "title": "Java Template", - "description": "Java template for the AsyncAPI Generator", + "title": "Node.js Websockets Template", + "description": "Node.js WebSockets template for the AsyncAPI Generator. It showcases how from a single AsyncAPI document you can generate a server and a client at the same time.", "links": { - "repoUrl": "https://github.com/asyncapi/java-template" + "repoUrl": "https://github.com/asyncapi/nodejs-ws-template" }, "filters": { - "language": [ - "javascript" - ], + "language": "javascript", "technology": [ - "Java" + "Node.js" ], "categories": [ "generator-template" @@ -780,15 +780,15 @@ } }, { - "title": "HTML Template", - "description": "HTML template for AsyncAPI Generator. Use it to generate a static docs. It is using AsyncAPI React component under the hood.", + "title": "Node.js Multiprotocol Template", + "description": "This template generates a server using your AsyncAPI document. It supports multiple different protocols, like Kafka or MQTT. It is designed in the way that generated code is a library and with it's API you can start the server, send messages or register a middleware for listening incoming messages. Runtime message validation included.", "links": { - "repoUrl": "https://github.com/asyncapi/html-template" + "repoUrl": "https://github.com/asyncapi/nodejs-template" }, "filters": { "language": "javascript", "technology": [ - "HTML" + "Node.js" ], "categories": [ "generator-template" diff --git a/config/tools.json b/config/tools.json index fdc5d42c7391..4735a84ccbce 100644 --- a/config/tools.json +++ b/config/tools.json @@ -1 +1 @@ -{"APIs":{"description":"The following is a list of APIs that expose functionality related to AsyncAPI.","toolsList":[{"title":"API Tracker - AsyncAPI specs","description":"Explore APIs and companies with public AsyncAPI specifications.","links":{"websiteUrl":"https://apitracker.io/specifications/asyncapi","repoUrl":""},"filters":{"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI Server API","description":"Server API providing official AsyncAPI tools","links":{"websiteUrl":"https://api.asyncapi.com/v1","docsUrl":"https://api.asyncapi.com/v1/docs","repoUrl":"https://github.com/asyncapi/server-api"},"filters":{"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["api"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI-Directory by APIs.guru","description":"Directory of asynchronous API specifications in AsyncAPI format.","links":{"websiteUrl":"https://apis.guru/asyncapi-directory/","repoUrl":"https://github.com/APIs-guru/asyncapi-directory"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"SIO-AsyncAPI","description":"This is code-first approach to generate AsyncAPI specification from Socket.IO server.","links":{"websiteUrl":"https://github.com/daler-rahimov/sio-asyncapi","docsUrl":"https://github.com/daler-rahimov/sio-asyncapi","repoUrl":"https://github.com/daler-rahimov/sio-asyncapi"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"technology":[{"name":"Socket.IO","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["code-first","api"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Code-first tools":{"description":"The following is a list of tools that generate AsyncAPI documents from your code.","toolsList":[{"title":"AsyncAPI.Net","description":"The AsyncAPI.NET SDK contains a useful object model for AsyncAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.","links":{"websiteUrl":"https://github.com/LEGO/AsyncAPI.NET/","repoUrl":"https://github.com/LEGO/AsyncAPI.NET"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["converters","code-first","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncApi.Net.Generator","description":"Code-first AsyncAPI documentation generator and ui","links":{"repoUrl":"https://github.com/yurvon-screamo/asyncapi.net"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"EventBridge Atlas","description":"Tool that translates your AWS EventBridge Schemas into an AsyncAPI document and a web UI.","links":{"websiteUrl":"https://eventbridge-atlas.netlify.app/","repoUrl":"https://github.com/boyney123/eventbridge-atlas"},"filters":{"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"FastStream","description":"A powerful and easy-to-use Python framework for building asynchronous services interacting with event streams such as Apache Kafka, RabbitMQ and NATS.","links":{"websiteUrl":"https://faststream.airt.ai","repoUrl":"https://github.com/airtai/FastStream"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"categories":["code-first","framework"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Go AsyncAPI","description":"This library helps to create AsyncAPI spec from your Go message structures. It uses reflection to translate Go structures in JSON Schema definitions and arrange them in AsyncAPI schema.","links":{"repoUrl":"https://github.com/swaggest/go-asyncapi"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Java AsyncAPI","description":"This tool stores modules, which simplifies interacting with AsyncAPI in jvm ecosystem.","links":{"repoUrl":"https://github.com/asyncapi/jasyncapi"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Kotlin","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"KnstEventBus","description":"AsyncApi code-first tools for c#. Generates document and view.","links":{"repoUrl":"https://github.com/d0972058277/KnstEventBus"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"},{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Kotlin AsyncAPI","description":"The Kotlin AsyncAPI project aims to provide convenience tools for generating and serving AsyncAPI documentation. The core of this project is a Kotlin DSL for building the specification in a typesafe way.","links":{"repoUrl":"https://github.com/OpenFolder/kotlin-asyncapi"},"filters":{"language":[{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"nestjs-asyncapi","description":"Utilize decorators to generate AsyncAPI document utilizing DTOs (similar to @nestjs/swagger) and a web UI.","links":{"repoUrl":"https://github.com/flamewow/nestjs-asyncapi"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Nest Js","color":"bg-[#E1224E]","borderColor":"border-[#B9012b]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Neuroglia AsyncAPI","description":"A .NET SDK for the Async API specification. Automatically generates and serves AsyncAPI documents based on your code. Includes fluent-builders to create AsyncAPI documents from scratch, and provides a web-based GUI to browse generated documents.","links":{"repoUrl":"https://github.com/neuroglia-io/AsyncApi"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Saunter","description":"Saunter is an AsyncAPI documentation generator for dotnet. Generates (and hosts) an AsyncAPI schema document from your code.","links":{"repoUrl":"https://github.com/tehmantra/saunter"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"SIO-AsyncAPI","description":"This is code-first approach to generate AsyncAPI specification from Socket.IO server.","links":{"websiteUrl":"https://github.com/daler-rahimov/sio-asyncapi","docsUrl":"https://github.com/daler-rahimov/sio-asyncapi","repoUrl":"https://github.com/daler-rahimov/sio-asyncapi"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"technology":[{"name":"Socket.IO","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["code-first","api"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Springwolf","description":"Automated documentation for async APIs built with Spring Boot. Like Springfox for AsyncAPI. Auto-generates an AsyncAPI document and a web UI.","links":{"websiteUrl":"https://www.springwolf.dev","repoUrl":"https://github.com/springwolf/springwolf-core"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"sttp tapir","description":"Library for describing HTTP endpoints, and then interpreting them as a server, client, or documentation","links":{"websiteUrl":"https://tapir.softwaremill.com/","repoUrl":"https://github.com/softwaremill/tapir"},"filters":{"language":[{"name":"Scala","color":"bg-[#FFA299]","borderColor":"border-[#DF301F]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Zod Sockets","description":"Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.","links":{"websiteUrl":"https://www.npmjs.com/package/zod-sockets","repoUrl":"https://github.com/RobinTail/zod-sockets"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","dsl","framework"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Code Generators":{"description":"The following is a list of tools that generate code from an AsyncAPI document; not the other way around.","toolsList":[{"title":"AsyncAPI Generator","description":"Generator is a tool that you can use to generate whatever you want basing on the AsyncAPI specification file as an input.","links":{"docsUrl":"https://www.asyncapi.com/docs/tools/generator","repoUrl":"https://github.com/asyncapi/generator"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["code-generator","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI Modelina","description":"Generate payload models into Java, TypeScript, Go, etc, you name it, from AsyncAPI documents. This tool gives you full control over the models through high customization","links":{"websiteUrl":"https://modelina.org","docsUrl":"https://github.com/asyncapi/modelina/tree/master/docs","repoUrl":"https://github.com/asyncapi/modelina"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"Docker","color":"bg-[#B8E0FF]","borderColor":"border-[#2596ED]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Golang AsyncAPI Code Generator","description":"Generate Go user and application boilerplate from AsyncAPI specifications. Can be called from `go generate` without requirements.\n","links":{"repoUrl":"https://github.com/lerenn/asyncapi-codegen"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"MultiAPI Generator","description":"This is a plugin designed to help developers automatizing the creation of code classes from YML files based on AsyncApi and OpenAPI. It is presented in 2 flavours Maven and Gradle","links":{"repoUrl":"https://github.com/sngular/scs-multiapi-plugin"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Groovy","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Node-RED AsyncAPI plugin","description":"A plugin for generating and configuring nodes for Kafka, MQTT, AMQP, etc. automatically from an AsyncAPI specification.","links":{"repoUrl":"https://github.com/dalelane/node-red-contrib-plugin-asyncapi"},"filters":{"technology":[{"name":"Node-RED","color":"bg-[#FF7474]","borderColor":"border-[#8F0101]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Converters":{"description":"The following is a list of tools that do not yet belong to any specific category but are also useful for the community.","toolsList":[{"title":"AsyncAPI-format","description":"Format an AsyncAPI document by ordering, casing, formatting, and filtering fields.","links":{"repoUrl":"https://github.com/thim81/asyncapi-format"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["converter","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI.Net","description":"The AsyncAPI.NET SDK contains a useful object model for AsyncAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.","links":{"websiteUrl":"https://github.com/LEGO/AsyncAPI.NET/","repoUrl":"https://github.com/LEGO/AsyncAPI.NET"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["converters","code-first","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Converter","description":"Converts old versions of AsyncAPI files into the latest version.","links":{"repoUrl":"https://github.com/asyncapi/converter-js"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["converter"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Converter-Go","description":"The AsyncAPI Converter converts AsyncAPI documents from versions 1.0.0, 1.1.0 and 1.2.0 to version 2.0.0. It supports both json and yaml formats on input and output. By default, the AsyncAPI Converter converts a document into the json format.","links":{"repoUrl":"https://github.com/asyncapi/converter-go"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["converter"],"hasCommercial":false,"isAsyncAPIOwner":true,"technology":[]}}]},"Directories":{"description":"The following is a list of directories that index public AsyncAPI documents.","toolsList":[{"title":"API Tracker - AsyncAPI specs","description":"Explore APIs and companies with public AsyncAPI specifications.","links":{"websiteUrl":"https://apitracker.io/specifications/asyncapi","repoUrl":""},"filters":{"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI-Directory by APIs.guru","description":"Directory of asynchronous API specifications in AsyncAPI format.","links":{"websiteUrl":"https://apis.guru/asyncapi-directory/","repoUrl":"https://github.com/APIs-guru/asyncapi-directory"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Documentation Generators":{"description":"The following is a list of tools that generate human-readable documentation from an AsyncAPI document.","toolsList":[{"title":"AsyncAPI Generator","description":"Generator is a tool that you can use to generate whatever you want basing on the AsyncAPI specification file as an input.","links":{"docsUrl":"https://www.asyncapi.com/docs/tools/generator","repoUrl":"https://github.com/asyncapi/generator"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Markdown","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"asyncapi-asciidoc-template","description":"Asciidoc template for the asyncapi generator","links":{"repoUrl":"https://gitlab.com/djencks/asyncapi-asciidoc-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"}],"categories":["documentation-generator","generator-template"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Bump.sh","description":"OpenAPI 2 & 3 / AsyncAPI 2 documentation generator, with automatic changelog and visual diff.","links":{"websiteUrl":"https://bump.sh/","repoUrl":""},"filters":{"categories":["documentation-generator"],"hasCommercial":true,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Cupid","description":"A library that focuses on finding and analyzing the relationships between AsyncAPI documents. It outputs a map of the system architecture.","links":{"repoUrl":"https://github.com/asyncapi/cupid"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"KnstEventBus","description":"AsyncApi code-first tools for c#. Generates document and view.","links":{"repoUrl":"https://github.com/d0972058277/KnstEventBus"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"},{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Springwolf","description":"Automated documentation for async APIs built with Spring Boot. Like Springfox for AsyncAPI. Auto-generates an AsyncAPI document and a web UI.","links":{"websiteUrl":"https://www.springwolf.dev","repoUrl":"https://github.com/springwolf/springwolf-core"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Widdershins","description":"OpenAPI 3.0 / Swagger 2.0 / AsyncAPI 1.0 definition to Slate / Shins compatible markdown.","links":{"websiteUrl":"https://mermade.github.io/reslate/","repoUrl":"https://github.com/Mermade/widdershins"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Shell","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Editors":{"description":"The following is a list of editors or related tools that allow editing of AsyncAPI document.","toolsList":[{"title":"AsyncAPI Studio","description":"Visually design your AsyncAPI files and event-driven architecture.","links":{"websiteUrl":"https://studio.asyncapi.com","repoUrl":"https://github.com/asyncapi/studio"},"filters":{"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["editor"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"UI components":{"description":"The following is a list of UI components to view AsyncAPI documents.","toolsList":[{"title":"Api-Diff-Viewer","description":"React component to view the difference between two Json based API documents. Supported specifications: JsonSchema, OpenAPI 3.x, AsyncAPI 2.x.","links":{"repoUrl":"https://github.com/udamir/api-diff-viewer","websiteUrl":"https://api-diff-viewer.vercel.app/"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"Babel","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Storybook","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ui-component"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI React component","description":"React component for rendering documentation from your specification in real-time in the browser. It also provides a WebComponent and bundle for Angular and Vue","links":{"repoUrl":"https://github.com/asyncapi/asyncapi-react"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"WebComponents","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ui-component"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"DSL":{"description":"Writing YAML by hand is no fun, and maybe you don't want a GUI, so use a Domain Specific Language to write AsyncAPI in your language of choice.","toolsList":[{"title":"BOATS","description":"Compile your single AsyncAPI file from multiple YAML files with BOATS and with the help of the template engine Nunjucks, plus a many extra helpers to automate much of the donkey work.","links":{"repoUrl":"https://github.com/j-d-carmichael/boats"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["dsl"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Zod Sockets","description":"Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.","links":{"websiteUrl":"https://www.npmjs.com/package/zod-sockets","repoUrl":"https://github.com/RobinTail/zod-sockets"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","dsl","framework"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Frameworks":{"description":"The following is a list of API/application frameworks that make use of AsyncAPI.","toolsList":[{"title":"Asynction","description":"SocketIO server framework driven by the AsyncAPI specification. Asynction guarantees that your API will work in accordance with its AsyncAPI documentation. Built on top of Flask-SocketIO.","links":{"websiteUrl":"https://pypi.org/project/asynction/","repoUrl":"https://github.com/dedoussis/asynction"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"technology":[{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["framework"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"FastStream","description":"A powerful and easy-to-use Python framework for building asynchronous services interacting with event streams such as Apache Kafka, RabbitMQ and NATS.","links":{"websiteUrl":"https://faststream.airt.ai","repoUrl":"https://github.com/airtai/FastStream"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"categories":["code-first","framework"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Zod Sockets","description":"Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.","links":{"websiteUrl":"https://www.npmjs.com/package/zod-sockets","repoUrl":"https://github.com/RobinTail/zod-sockets"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","dsl","framework"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"GitHub Actions":{"description":"The following is a list of GitHub Actions that you can use in your workflows","toolsList":[{"title":"API documentation generation on Bump.sh","description":"With this GitHub Action you can automatically generate your API reference (with the changelog and diff) on Bump.sh from any AsyncAPI file.","links":{"websiteUrl":"https://github.com/marketplace/actions/api-documentation-on-bump","repoUrl":"https://github.com/bump-sh/github-action"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI GitHub Action","description":"This action validates if the AsyncAPI schema file is valid or not.","links":{"websiteUrl":"https://github.com/marketplace/actions/asyncapi-github-action","repoUrl":"https://github.com/WaleedAshraf/asyncapi-github-action"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["github-action","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Automated version bump for AsyncAPI documents","description":"With this GitHub Action, you can automatically bump the version based on commit messages, which is similar to what semantic-release is for NPM.","links":{"websiteUrl":"https://github.com/marketplace/actions/automated-version-bump-for-asyncapi","repoUrl":"https://github.com/bump-sh/github-action"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"GitHub Action for CLI","description":"GitHub Action with generator, validator, converter and others - all in one for your AsyncAPI documents with AsyncAPI CLI as backbone","links":{"repoUrl":"https://github.com/asyncapi/github-action-for-cli"},"filters":{"technology":[{"name":"AsyncAPI Generator","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"GitHub Action for Generator","description":null,"links":{"repoUrl":"https://github.com/actions-marketplace-validations/asyncapi_github-action-for-generator"},"filters":{"technology":[{"name":"AsyncAPI Generator","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Mocking and Testing":{"description":"The tools below take specification documents as input, then publish fake messages to broker destinations for simulation purposes. They may also check that publisher messages are compliant with schemas.","toolsList":[{"title":"Microcks","description":"Mocking and testing platform for API and microservices. Turn your AsyncAPI, OpenAPI contract examples, or Postman collections into ready-to-use mocks. Use examples to simulate and validate received messages according to schema elements.","links":{"websiteUrl":"https://microcks.io/","repoUrl":"https://github.com/microcks/microcks"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Kubernetes-native","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Saas","color":"bg-[#6AB8EC]","borderColor":"border-[#2275AD]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"MultiAPI Converter","description":"Use AsyncAPI definition, to generate Spring Cloud Contract producer validation or consumer stubs, using maven.","links":{"repoUrl":"https://github.com/sngular/scc-multiapi-converter"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Specmatic","description":"An API contract testing tool that helps ensure the correctness APIs by automatically generating test cases and verifying them against the API spec. It simplifies the process of testing APIs and reduces the likelihood of bugs and compatibility issues.","links":{"websiteUrl":"https://specmatic.io","docsUrl":"https://specmatic.io/documentation/","repoUrl":"https://github.com/znsio/specmatic"},"filters":{"language":[{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Virtualan","description":"Mocking and testing platform for API and microservices. Allows you to create and setup mocks for OpenAPI and AsyncAPI contracts. Shows how to setup and create AsyncAPI GitHub Reference Examples and OpenAPI GitHub Reference Examples.","links":{"websiteUrl":"https://www.virtualan.io/index.html","repoUrl":"https://github.com/virtualansoftware"},"filters":{"technology":[{"name":"Kubernetes-native","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Validators":{"description":"The following is a list of tools that validate AsyncAPI documents.","toolsList":[{"title":"AMF","description":"AMF (AML Modeling Framework) is an open-source library capable of parsing and validating AML metadata documents.","links":{"docsUrl":"https://a.ml/docs/","repoUrl":"https://github.com/aml-org/amf"},"filters":{"language":[{"name":"Scala","color":"bg-[#FFA299]","borderColor":"border-[#DF301F]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI GitHub Action","description":"This action validates if the AsyncAPI schema file is valid or not.","links":{"websiteUrl":"https://github.com/marketplace/actions/asyncapi-github-action","repoUrl":"https://github.com/WaleedAshraf/asyncapi-github-action"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["github-action","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI Parser","description":"Use this package to parse and validate AsyncAPI documents —either YAML or JSON— in your Node.js or browser application. Updated bundle for the browser is always attached to the GitHub Release.","links":{"repoUrl":"https://github.com/asyncapi/parser-js"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI Parser","description":"The AsyncAPI Parser validates AsyncAPI documents according to dedicated schemas.","links":{"repoUrl":"https://github.com/asyncapi/parser-go"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":true,"technology":[]}},{"title":"AsyncAPI Parser Wrapper","description":"Use this library to parse and validate AsyncAPI documents — either YAML or JSON — in your Java application. It is a Java wrapper over JavaScript Parser implemented using J2V8.","links":{"repoUrl":"https://github.com/AsyncAPITools/parser-java-wrapper"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI Validation","description":"Message validation package for YAML and JSON AsyncAPI documents.","links":{"repoUrl":"https://github.com/Elhebert/asyncapi-validation"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"asyncapi-validator","description":"It allows you to validate the schema of your messages against your AsyncAPI schema definition. You can use it with Kafka, RabbitMQ or any other messaging/queue.","links":{"repoUrl":"https://github.com/WaleedAshraf/asyncapi-validator"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI.Net","description":"The AsyncAPI.NET SDK contains a useful object model for AsyncAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.","links":{"websiteUrl":"https://github.com/LEGO/AsyncAPI.NET/","repoUrl":"https://github.com/LEGO/AsyncAPI.NET"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["converters","code-first","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Spectral","description":"A flexible JSON/YAML linter for creating automated style guides, with baked in support for OpenAPI v3.1, v3.0, and v2.0 as well as AsyncAPI v2.x.","links":{"repoUrl":"https://github.com/stoplightio/spectral"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Compare tools":{"description":"The following is a list of tools that compare AsyncAPI documents.","toolsList":[{"title":"Api-Smart-Diff","description":"It allows you to compare two API documents and classify changes. Supported API specifications: OpenAPI, AsyncAPI, JsonSchema.","links":{"repoUrl":"https://github.com/udamir/api-smart-diff"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI Diff","description":"Diff is a library that compares two AsyncAPI Documents and provides information about the differences by pointing out explicitly information like breaking changes.","links":{"repoUrl":"https://github.com/asyncapi/diff"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"jasyncapicmp","description":"Tool for comparing two AsyncAPI versions and evaluating compatibility.","links":{"websiteUrl":"https://siom79.github.io/jasyncapicmp/","docsUrl":"https://github.com/siom79/jasyncapicmp","repoUrl":"https://github.com/siom79/jasyncapicmp"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"jasyncapicmp","description":"Tool/library/maven-plugin for comparing two AsyncAPI versions and evaluating compatibility.","links":{"websiteUrl":"https://siom79.github.io/jasyncapicmp/","repoUrl":"https://github.com/siom79/jasyncapicmp"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"CLIs":{"description":"The following is a list of tools that you can work with in terminal or do some CI/CD automation.","toolsList":[{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/asyncapi/cli"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/hkirat/asyncapi-fork"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI-format","description":"Format an AsyncAPI document by ordering, casing, formatting, and filtering fields.","links":{"repoUrl":"https://github.com/asyncapi/converter-go"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["converter","cli"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Bundlers":{"description":"The following is a list of tools that you can work with to bundle AsyncAPI documents.","toolsList":[{"title":"Api-ref-bundler","description":"It allows you bundle/dereference external/internal $refs in Json based API document. Supported specifications: OpenAPI, AsyncAPI, JsonSchema.","links":{"repoUrl":"https://github.com/udamir/api-ref-bundler"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["bundler"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI Bundler","description":"Combine multiple AsyncAPI specification files into one.","links":{"repoUrl":"https://github.com/asyncapi/bundler"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["bundler"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"IDE Extensions":{"description":"The following is a list of extensions for different IDEs like VSCode, IntelliJ IDEA and others","toolsList":[{"title":"asyncapi-preview","description":"VSCode extension that enables you to:\n - Preview documentation generated using you AsyncAPI document. It uses AsyncAPI React component under the hood,\n - Create AsyncAPI documents faster using SmartPaste functionality\n","links":{"repoUrl":"https://github.com/asyncapi/vs-asyncapi-preview"},"filters":{"technology":[{"name":"VSCode","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"SmartPaste","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ide-extension"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"asyncapi-preview","description":"VSCode extension that enables you to:\n - Preview documentation generated using you AsyncAPI document. It uses AsyncAPI React component under the hood,\n - Create AsyncAPI documents faster using SmartPaste functionality\n","links":{"repoUrl":"https://github.com/Savio629/testing2"},"filters":{"technology":[{"name":"VSCode","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"SmartPaste","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ide-extension"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"jAsyncAPI - IDEA plugin","description":"Idea plugin for the java-asyncapi - Helps to edit and validate AsyncAPI schemas.","links":{"websiteUrl":"https://plugins.jetbrains.com/plugin/15673-asyncapi","docsUrl":"https://github.com/asyncapi/jasyncapi-idea-plugin#usage","repoUrl":"https://github.com/asyncapi/jasyncapi-idea-plugin"},"filters":{"language":[{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"}],"technology":[{"name":"JetBrains","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"IntelliJ IDEA","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ide-extension"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"AsyncAPI Generator Templates":{"description":"The following is a list of templates compatible with AsyncAPI Generator. You can use them to generate apps, clients or documentation from your AsyncAPI documents.","toolsList":[{"title":"HTML Template","description":"HTML template for AsyncAPI Generator. Use it to generate a static docs. It is using AsyncAPI React component under the hood.","links":{"repoUrl":"https://github.com/asyncapi/html-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"HTML","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Java Spring Template","description":"Java Spring template for the AsyncAPI Generator","links":{"repoUrl":"https://github.com/asyncapi/java-spring-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Java Template","description":"Java template for the AsyncAPI Generator","links":{"repoUrl":"https://github.com/asyncapi/java-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Java","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Node.js Multiprotocol Template","description":"This template generates a server using your AsyncAPI document. It supports multiple different protocols, like Kafka or MQTT. It is designed in the way that generated code is a library and with it's API you can start the server, send messages or register a middleware for listening incoming messages. Runtime message validation included.","links":{"repoUrl":"https://github.com/asyncapi/nodejs-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Node.js Websockets Template","description":"Node.js WebSockets template for the AsyncAPI Generator. It showcases how from a single AsyncAPI document you can generate a server and a client at the same time.","links":{"repoUrl":"https://github.com/asyncapi/nodejs-ws-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"Others":{"description":"The following is a list of tools that comes under Other category.","toolsList":[{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/asyncapi/cli"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/hkirat/asyncapi-fork"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]}} \ No newline at end of file +{"APIs":{"description":"The following is a list of APIs that expose functionality related to AsyncAPI.","toolsList":[{"title":"API Tracker - AsyncAPI specs","description":"Explore APIs and companies with public AsyncAPI specifications.","links":{"websiteUrl":"https://apitracker.io/specifications/asyncapi","repoUrl":""},"filters":{"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI Server API","description":"Server API providing official AsyncAPI tools","links":{"websiteUrl":"https://api.asyncapi.com/v1","docsUrl":"https://api.asyncapi.com/v1/docs","repoUrl":"https://github.com/asyncapi/server-api"},"filters":{"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["api"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI-Directory by APIs.guru","description":"Directory of asynchronous API specifications in AsyncAPI format.","links":{"websiteUrl":"https://apis.guru/asyncapi-directory/","repoUrl":"https://github.com/APIs-guru/asyncapi-directory"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"SIO-AsyncAPI","description":"This is code-first approach to generate AsyncAPI specification from Socket.IO server.","links":{"websiteUrl":"https://github.com/daler-rahimov/sio-asyncapi","docsUrl":"https://github.com/daler-rahimov/sio-asyncapi","repoUrl":"https://github.com/daler-rahimov/sio-asyncapi"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"technology":[{"name":"Socket.IO","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["code-first","api"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Code-first tools":{"description":"The following is a list of tools that generate AsyncAPI documents from your code.","toolsList":[{"title":"AsyncAPI.Net","description":"The AsyncAPI.NET SDK contains a useful object model for AsyncAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.","links":{"websiteUrl":"https://github.com/LEGO/AsyncAPI.NET/","repoUrl":"https://github.com/LEGO/AsyncAPI.NET"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["converters","code-first","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncApi.Net.Generator","description":"Code-first AsyncAPI documentation generator and ui","links":{"repoUrl":"https://github.com/yurvon-screamo/asyncapi.net"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"EventBridge Atlas","description":"Tool that translates your AWS EventBridge Schemas into an AsyncAPI document and a web UI.","links":{"websiteUrl":"https://eventbridge-atlas.netlify.app/","repoUrl":"https://github.com/boyney123/eventbridge-atlas"},"filters":{"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"FastStream","description":"A powerful and easy-to-use Python framework for building asynchronous services interacting with event streams such as Apache Kafka, RabbitMQ and NATS.","links":{"websiteUrl":"https://faststream.airt.ai","repoUrl":"https://github.com/airtai/FastStream"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"categories":["code-first","framework"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Go AsyncAPI","description":"This library helps to create AsyncAPI spec from your Go message structures. It uses reflection to translate Go structures in JSON Schema definitions and arrange them in AsyncAPI schema.","links":{"repoUrl":"https://github.com/swaggest/go-asyncapi"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Java AsyncAPI","description":"This tool stores modules, which simplifies interacting with AsyncAPI in jvm ecosystem.","links":{"repoUrl":"https://github.com/asyncapi/jasyncapi"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Kotlin","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"KnstEventBus","description":"AsyncApi code-first tools for c#. Generates document and view.","links":{"repoUrl":"https://github.com/d0972058277/KnstEventBus"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"},{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Kotlin AsyncAPI","description":"The Kotlin AsyncAPI project aims to provide convenience tools for generating and serving AsyncAPI documentation. The core of this project is a Kotlin DSL for building the specification in a typesafe way.","links":{"repoUrl":"https://github.com/OpenFolder/kotlin-asyncapi"},"filters":{"language":[{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"nestjs-asyncapi","description":"Utilize decorators to generate AsyncAPI document utilizing DTOs (similar to @nestjs/swagger) and a web UI.","links":{"repoUrl":"https://github.com/flamewow/nestjs-asyncapi"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Nest Js","color":"bg-[#E1224E]","borderColor":"border-[#B9012b]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Neuroglia AsyncAPI","description":"A .NET SDK for the Async API specification. Automatically generates and serves AsyncAPI documents based on your code. Includes fluent-builders to create AsyncAPI documents from scratch, and provides a web-based GUI to browse generated documents.","links":{"repoUrl":"https://github.com/neuroglia-io/AsyncApi"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Saunter","description":"Saunter is an AsyncAPI documentation generator for dotnet. Generates (and hosts) an AsyncAPI schema document from your code.","links":{"repoUrl":"https://github.com/tehmantra/saunter"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"SIO-AsyncAPI","description":"This is code-first approach to generate AsyncAPI specification from Socket.IO server.","links":{"websiteUrl":"https://github.com/daler-rahimov/sio-asyncapi","docsUrl":"https://github.com/daler-rahimov/sio-asyncapi","repoUrl":"https://github.com/daler-rahimov/sio-asyncapi"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"technology":[{"name":"Socket.IO","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["code-first","api"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Springwolf","description":"Automated documentation for async APIs built with Spring Boot. Like Springfox for AsyncAPI. Auto-generates an AsyncAPI document and a web UI.","links":{"websiteUrl":"https://www.springwolf.dev","repoUrl":"https://github.com/springwolf/springwolf-core"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"sttp tapir","description":"Library for describing HTTP endpoints, and then interpreting them as a server, client, or documentation","links":{"websiteUrl":"https://tapir.softwaremill.com/","repoUrl":"https://github.com/softwaremill/tapir"},"filters":{"language":[{"name":"Scala","color":"bg-[#FFA299]","borderColor":"border-[#DF301F]"}],"categories":["code-first"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Zod Sockets","description":"Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.","links":{"websiteUrl":"https://www.npmjs.com/package/zod-sockets","repoUrl":"https://github.com/RobinTail/zod-sockets"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","dsl","framework"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Code Generators":{"description":"The following is a list of tools that generate code from an AsyncAPI document; not the other way around.","toolsList":[{"title":"AsyncAPI Generator","description":"Generator is a tool that you can use to generate whatever you want basing on the AsyncAPI specification file as an input.","links":{"docsUrl":"https://www.asyncapi.com/docs/tools/generator","repoUrl":"https://github.com/asyncapi/generator"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["code-generator","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI Modelina","description":"Generate payload models into Java, TypeScript, Go, etc, you name it, from AsyncAPI documents. This tool gives you full control over the models through high customization","links":{"websiteUrl":"https://modelina.org","docsUrl":"https://github.com/asyncapi/modelina/tree/master/docs","repoUrl":"https://github.com/asyncapi/modelina"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"Docker","color":"bg-[#B8E0FF]","borderColor":"border-[#2596ED]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Golang AsyncAPI Code Generator","description":"Generate Go user and application boilerplate from AsyncAPI specifications. Can be called from `go generate` without requirements.\n","links":{"repoUrl":"https://github.com/lerenn/asyncapi-codegen"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"MultiAPI Generator","description":"This is a plugin designed to help developers automatizing the creation of code classes from YML files based on AsyncApi and OpenAPI. It is presented in 2 flavours Maven and Gradle","links":{"repoUrl":"https://github.com/sngular/scs-multiapi-plugin"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Groovy","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Node-RED AsyncAPI plugin","description":"A plugin for generating and configuring nodes for Kafka, MQTT, AMQP, etc. automatically from an AsyncAPI specification.","links":{"repoUrl":"https://github.com/dalelane/node-red-contrib-plugin-asyncapi"},"filters":{"technology":[{"name":"Node-RED","color":"bg-[#FF7474]","borderColor":"border-[#8F0101]"}],"categories":["code-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Converters":{"description":"The following is a list of tools that do not yet belong to any specific category but are also useful for the community.","toolsList":[{"title":"AsyncAPI-format","description":"Format an AsyncAPI document by ordering, casing, formatting, and filtering fields.","links":{"repoUrl":"https://github.com/thim81/asyncapi-format"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["converter","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI.Net","description":"The AsyncAPI.NET SDK contains a useful object model for AsyncAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.","links":{"websiteUrl":"https://github.com/LEGO/AsyncAPI.NET/","repoUrl":"https://github.com/LEGO/AsyncAPI.NET"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["converters","code-first","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Converter","description":"Converts old versions of AsyncAPI files into the latest version.","links":{"repoUrl":"https://github.com/asyncapi/converter-js"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["converter"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Converter-Go","description":"The AsyncAPI Converter converts AsyncAPI documents from versions 1.0.0, 1.1.0 and 1.2.0 to version 2.0.0. It supports both json and yaml formats on input and output. By default, the AsyncAPI Converter converts a document into the json format.","links":{"repoUrl":"https://github.com/asyncapi/converter-go"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["converter"],"hasCommercial":false,"isAsyncAPIOwner":true,"technology":[]}}]},"Directories":{"description":"The following is a list of directories that index public AsyncAPI documents.","toolsList":[{"title":"API Tracker - AsyncAPI specs","description":"Explore APIs and companies with public AsyncAPI specifications.","links":{"websiteUrl":"https://apitracker.io/specifications/asyncapi","repoUrl":""},"filters":{"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI-Directory by APIs.guru","description":"Directory of asynchronous API specifications in AsyncAPI format.","links":{"websiteUrl":"https://apis.guru/asyncapi-directory/","repoUrl":"https://github.com/APIs-guru/asyncapi-directory"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["api","directory"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Documentation Generators":{"description":"The following is a list of tools that generate human-readable documentation from an AsyncAPI document.","toolsList":[{"title":"AsyncAPI Generator","description":"Generator is a tool that you can use to generate whatever you want basing on the AsyncAPI specification file as an input.","links":{"docsUrl":"https://www.asyncapi.com/docs/tools/generator","repoUrl":"https://github.com/asyncapi/generator"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Markdown","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"asyncapi-asciidoc-template","description":"Asciidoc template for the asyncapi generator","links":{"repoUrl":"https://gitlab.com/djencks/asyncapi-asciidoc-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"}],"categories":["documentation-generator","generator-template"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Bump.sh","description":"OpenAPI 2 & 3 / AsyncAPI 2 documentation generator, with automatic changelog and visual diff.","links":{"websiteUrl":"https://bump.sh/","docsUrl":"https://docs.bump.sh/help/","repoUrl":""},"filters":{"categories":["documentation-generator"],"hasCommercial":true,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Cupid","description":"A library that focuses on finding and analyzing the relationships between AsyncAPI documents. It outputs a map of the system architecture.","links":{"repoUrl":"https://github.com/asyncapi/cupid"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"KnstEventBus","description":"AsyncApi code-first tools for c#. Generates document and view.","links":{"repoUrl":"https://github.com/d0972058277/KnstEventBus"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"},{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Springwolf","description":"Automated documentation for async APIs built with Spring Boot. Like Springfox for AsyncAPI. Auto-generates an AsyncAPI document and a web UI.","links":{"websiteUrl":"https://www.springwolf.dev","repoUrl":"https://github.com/springwolf/springwolf-core"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Widdershins","description":"OpenAPI 3.0 / Swagger 2.0 / AsyncAPI 1.0 definition to Slate / Shins compatible markdown.","links":{"websiteUrl":"https://mermade.github.io/reslate/","repoUrl":"https://github.com/Mermade/widdershins"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"Shell","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["documentation-generator"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Editors":{"description":"The following is a list of editors or related tools that allow editing of AsyncAPI document.","toolsList":[{"title":"AsyncAPI Studio","description":"Visually design your AsyncAPI files and event-driven architecture.","links":{"websiteUrl":"https://studio.asyncapi.com","repoUrl":"https://github.com/asyncapi/studio"},"filters":{"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["editor"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"UI components":{"description":"The following is a list of UI components to view AsyncAPI documents.","toolsList":[{"title":"Api-Diff-Viewer","description":"React component to view the difference between two Json based API documents. Supported specifications: JsonSchema, OpenAPI 3.x, AsyncAPI 2.x.","links":{"repoUrl":"https://github.com/udamir/api-diff-viewer","websiteUrl":"https://api-diff-viewer.vercel.app/"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"Babel","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Storybook","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ui-component"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI React component","description":"React component for rendering documentation from your specification in real-time in the browser. It also provides a WebComponent and bundle for Angular and Vue","links":{"repoUrl":"https://github.com/asyncapi/asyncapi-react"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"React JS","color":"bg-[#9FECFA]","borderColor":"border-[#08D8FE]"},{"name":"WebComponents","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ui-component"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"DSL":{"description":"Writing YAML by hand is no fun, and maybe you don't want a GUI, so use a Domain Specific Language to write AsyncAPI in your language of choice.","toolsList":[{"title":"BOATS","description":"Compile your single AsyncAPI file from multiple YAML files with BOATS and with the help of the template engine Nunjucks, plus a many extra helpers to automate much of the donkey work.","links":{"repoUrl":"https://github.com/j-d-carmichael/boats"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["dsl"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Zod Sockets","description":"Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.","links":{"websiteUrl":"https://www.npmjs.com/package/zod-sockets","repoUrl":"https://github.com/RobinTail/zod-sockets"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","dsl","framework"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Frameworks":{"description":"The following is a list of API/application frameworks that make use of AsyncAPI.","toolsList":[{"title":"Asynction","description":"SocketIO server framework driven by the AsyncAPI specification. Asynction guarantees that your API will work in accordance with its AsyncAPI documentation. Built on top of Flask-SocketIO.","links":{"websiteUrl":"https://pypi.org/project/asynction/","repoUrl":"https://github.com/dedoussis/asynction"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"technology":[{"name":"Flask","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["framework"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"FastStream","description":"A powerful and easy-to-use Python framework for building asynchronous services interacting with event streams such as Apache Kafka, RabbitMQ and NATS.","links":{"websiteUrl":"https://faststream.airt.ai","repoUrl":"https://github.com/airtai/FastStream"},"filters":{"language":[{"name":"Python","color":"bg-[#A8D0EF]","borderColor":"border-[#3878AB]"}],"categories":["code-first","framework"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"Zod Sockets","description":"Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers.","links":{"websiteUrl":"https://www.npmjs.com/package/zod-sockets","repoUrl":"https://github.com/RobinTail/zod-sockets"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"},{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-first","dsl","framework"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"GitHub Actions":{"description":"The following is a list of GitHub Actions that you can use in your workflows","toolsList":[{"title":"API documentation generation on Bump.sh","description":"With this GitHub Action you can automatically generate your API reference (with the changelog and diff) on Bump.sh from any AsyncAPI file.","links":{"websiteUrl":"https://github.com/marketplace/actions/api-documentation-on-bump","repoUrl":"https://github.com/bump-sh/github-action"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI GitHub Action","description":"This action validates if the AsyncAPI schema file is valid or not.","links":{"websiteUrl":"https://github.com/marketplace/actions/asyncapi-github-action","repoUrl":"https://github.com/WaleedAshraf/asyncapi-github-action"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["github-action","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Automated version bump for AsyncAPI documents","description":"With this GitHub Action, you can automatically bump the version based on commit messages, which is similar to what semantic-release is for NPM.","links":{"websiteUrl":"https://github.com/marketplace/actions/automated-version-bump-for-asyncapi","repoUrl":"https://github.com/bump-sh/github-action"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"GitHub Action for CLI","description":"GitHub Action with generator, validator, converter and others - all in one for your AsyncAPI documents with AsyncAPI CLI as backbone","links":{"repoUrl":"https://github.com/asyncapi/github-action-for-cli"},"filters":{"technology":[{"name":"AsyncAPI Generator","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"GitHub Action for Generator","description":null,"links":{"repoUrl":"https://github.com/actions-marketplace-validations/asyncapi_github-action-for-generator"},"filters":{"technology":[{"name":"AsyncAPI Generator","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["github-action"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Mocking and Testing":{"description":"The tools below take specification documents as input, then publish fake messages to broker destinations for simulation purposes. They may also check that publisher messages are compliant with schemas.","toolsList":[{"title":"Microcks","description":"Mocking and testing platform for API and microservices. Turn your AsyncAPI, OpenAPI contract examples, or Postman collections into ready-to-use mocks. Use examples to simulate and validate received messages according to schema elements.","links":{"websiteUrl":"https://microcks.io/","repoUrl":"https://github.com/microcks/microcks"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Kubernetes-native","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"},{"name":"Saas","color":"bg-[#6AB8EC]","borderColor":"border-[#2275AD]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"MultiAPI Converter","description":"Use AsyncAPI definition, to generate Spring Cloud Contract producer validation or consumer stubs, using maven.","links":{"repoUrl":"https://github.com/sngular/scc-multiapi-converter"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Specmatic","description":"An API contract testing tool that helps ensure the correctness APIs by automatically generating test cases and verifying them against the API spec. It simplifies the process of testing APIs and reduces the likelihood of bugs and compatibility issues.","links":{"websiteUrl":"https://specmatic.io","docsUrl":"https://specmatic.io/documentation/","repoUrl":"https://github.com/znsio/specmatic"},"filters":{"language":[{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Virtualan","description":"Mocking and testing platform for API and microservices. Allows you to create and setup mocks for OpenAPI and AsyncAPI contracts. Shows how to setup and create AsyncAPI GitHub Reference Examples and OpenAPI GitHub Reference Examples.","links":{"websiteUrl":"https://www.virtualan.io/index.html","repoUrl":"https://github.com/virtualansoftware"},"filters":{"technology":[{"name":"Kubernetes-native","color":"bg-[#D7C7F2]","borderColor":"border-[#A387D2]"}],"categories":["mocking-and-testing"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Validators":{"description":"The following is a list of tools that validate AsyncAPI documents.","toolsList":[{"title":"AMF","description":"AMF (AML Modeling Framework) is an open-source library capable of parsing and validating AML metadata documents.","links":{"docsUrl":"https://a.ml/docs/","repoUrl":"https://github.com/aml-org/amf"},"filters":{"language":[{"name":"Scala","color":"bg-[#FFA299]","borderColor":"border-[#DF301F]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI GitHub Action","description":"This action validates if the AsyncAPI schema file is valid or not.","links":{"websiteUrl":"https://github.com/marketplace/actions/asyncapi-github-action","repoUrl":"https://github.com/WaleedAshraf/asyncapi-github-action"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["github-action","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI Parser","description":"Use this package to parse and validate AsyncAPI documents —either YAML or JSON— in your Node.js or browser application. Updated bundle for the browser is always attached to the GitHub Release.","links":{"repoUrl":"https://github.com/asyncapi/parser-js"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI Parser","description":"The AsyncAPI Parser validates AsyncAPI documents according to dedicated schemas.","links":{"repoUrl":"https://github.com/asyncapi/parser-go"},"filters":{"language":[{"name":"Go/Golang","color":"bg-[#8ECFDF]","borderColor":"border-[#00AFD9]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":true,"technology":[]}},{"title":"AsyncAPI Parser Wrapper","description":"Use this library to parse and validate AsyncAPI documents — either YAML or JSON — in your Java application. It is a Java wrapper over JavaScript Parser implemented using J2V8.","links":{"repoUrl":"https://github.com/AsyncAPITools/parser-java-wrapper"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI Validation","description":"Message validation package for YAML and JSON AsyncAPI documents.","links":{"repoUrl":"https://github.com/Elhebert/asyncapi-validation"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"asyncapi-validator","description":"It allows you to validate the schema of your messages against your AsyncAPI schema definition. You can use it with Kafka, RabbitMQ or any other messaging/queue.","links":{"repoUrl":"https://github.com/WaleedAshraf/asyncapi-validator"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI.Net","description":"The AsyncAPI.NET SDK contains a useful object model for AsyncAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.","links":{"websiteUrl":"https://github.com/LEGO/AsyncAPI.NET/","repoUrl":"https://github.com/LEGO/AsyncAPI.NET"},"filters":{"language":[{"name":"C#","color":"bg-[#E3AFE0]","borderColor":"border-[#9B4F96]"}],"technology":[{"name":".NET","color":"bg-[#A184FF]","borderColor":"border-[#5026D4]"},{"name":"ASP.NET","color":"bg-[#71C2FB]","borderColor":"border-[#1577BC]"}],"categories":["converters","code-first","validator"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"Spectral","description":"A flexible JSON/YAML linter for creating automated style guides, with baked in support for OpenAPI v3.1, v3.0, and v2.0 as well as AsyncAPI v2.x.","links":{"repoUrl":"https://github.com/stoplightio/spectral"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["validator"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Compare tools":{"description":"The following is a list of tools that compare AsyncAPI documents.","toolsList":[{"title":"Api-Smart-Diff","description":"It allows you to compare two API documents and classify changes. Supported API specifications: OpenAPI, AsyncAPI, JsonSchema.","links":{"repoUrl":"https://github.com/udamir/api-smart-diff"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":false,"technology":[]}},{"title":"AsyncAPI Diff","description":"Diff is a library that compares two AsyncAPI Documents and provides information about the differences by pointing out explicitly information like breaking changes.","links":{"repoUrl":"https://github.com/asyncapi/diff"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"jasyncapicmp","description":"Tool for comparing two AsyncAPI versions and evaluating compatibility.","links":{"websiteUrl":"https://siom79.github.io/jasyncapicmp/","docsUrl":"https://github.com/siom79/jasyncapicmp","repoUrl":"https://github.com/siom79/jasyncapicmp"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"jasyncapicmp","description":"Tool/library/maven-plugin for comparing two AsyncAPI versions and evaluating compatibility.","links":{"websiteUrl":"https://siom79.github.io/jasyncapicmp/","repoUrl":"https://github.com/siom79/jasyncapicmp"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"}],"categories":["compare-tool"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"CLIs":{"description":"The following is a list of tools that you can work with in terminal or do some CI/CD automation.","toolsList":[{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/asyncapi/cli"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/hkirat/asyncapi-fork"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI-format","description":"Format an AsyncAPI document by ordering, casing, formatting, and filtering fields.","links":{"repoUrl":"https://github.com/asyncapi/converter-go"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["converter","cli"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"ZenWave SDK","description":"DDD and API-First for Event-Driven Microservices","links":{"websiteUrl":"https://zenwave360.github.io/","docsUrl":"https://zenwave360.github.io/zenwave-sdk/plugins/asyncapi-spring-cloud-streams3/","repoUrl":"https://github.com/zenwave360/zenwave-sdk"},"filters":{"language":[{"name":"Java","color":"bg-[#ECA2A4]","borderColor":"border-[#EC2125]"}],"technology":[{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Liquid","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"Spring Cloud Streams","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"JHipster JDL","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["code-generator","dsl","mocking-and-testing","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]},"Bundlers":{"description":"The following is a list of tools that you can work with to bundle AsyncAPI documents.","toolsList":[{"title":"Api-ref-bundler","description":"It allows you bundle/dereference external/internal $refs in Json based API document. Supported specifications: OpenAPI, AsyncAPI, JsonSchema.","links":{"repoUrl":"https://github.com/udamir/api-ref-bundler"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["bundler"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"AsyncAPI Bundler","description":"Combine multiple AsyncAPI specification files into one.","links":{"repoUrl":"https://github.com/asyncapi/bundler"},"filters":{"language":[{"name":"TypeScript","color":"bg-[#7DBCFE]","borderColor":"border-[#2C78C7]"}],"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["bundler"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"IDE Extensions":{"description":"The following is a list of extensions for different IDEs like VSCode, IntelliJ IDEA and others","toolsList":[{"title":"asyncapi-preview","description":"VSCode extension that enables you to:\n - Preview documentation generated using you AsyncAPI document. It uses AsyncAPI React component under the hood,\n - Create AsyncAPI documents faster using SmartPaste functionality\n","links":{"repoUrl":"https://github.com/asyncapi/vs-asyncapi-preview"},"filters":{"technology":[{"name":"VSCode","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"SmartPaste","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ide-extension"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"asyncapi-preview","description":"VSCode extension that enables you to:\n - Preview documentation generated using you AsyncAPI document. It uses AsyncAPI React component under the hood,\n - Create AsyncAPI documents faster using SmartPaste functionality\n","links":{"repoUrl":"https://github.com/Savio629/testing2"},"filters":{"technology":[{"name":"VSCode","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"SmartPaste","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ide-extension"],"hasCommercial":false,"isAsyncAPIOwner":false}},{"title":"jAsyncAPI - IDEA plugin","description":"Idea plugin for the java-asyncapi - Helps to edit and validate AsyncAPI schemas.","links":{"websiteUrl":"https://plugins.jetbrains.com/plugin/15673-asyncapi","docsUrl":"https://github.com/asyncapi/jasyncapi-idea-plugin#usage","repoUrl":"https://github.com/asyncapi/jasyncapi-idea-plugin"},"filters":{"language":[{"name":"Kotlin","color":"bg-[#B1ACDF]","borderColor":"border-[#756BD9]"}],"technology":[{"name":"JetBrains","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"},{"name":"IntelliJ IDEA","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["ide-extension"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"AsyncAPI Generator Templates":{"description":"The following is a list of templates compatible with AsyncAPI Generator. You can use them to generate apps, clients or documentation from your AsyncAPI documents.","toolsList":[{"title":"HTML Template","description":"HTML template for AsyncAPI Generator. Use it to generate a static docs. It is using AsyncAPI React component under the hood.","links":{"repoUrl":"https://github.com/asyncapi/html-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"HTML","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Java Spring Template","description":"Java Spring template for the AsyncAPI Generator","links":{"repoUrl":"https://github.com/asyncapi/java-spring-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Springboot","color":"bg-[#98E279]","borderColor":"border-[#68BC44]"},{"name":"Maven","color":"bg-[#FF6B80]","borderColor":"border-[#CA1A33]"},{"name":"Gradle","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Java Template","description":"Java template for the AsyncAPI Generator","links":{"repoUrl":"https://github.com/asyncapi/java-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Java","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Node.js Multiprotocol Template","description":"This template generates a server using your AsyncAPI document. It supports multiple different protocols, like Kafka or MQTT. It is designed in the way that generated code is a library and with it's API you can start the server, send messages or register a middleware for listening incoming messages. Runtime message validation included.","links":{"repoUrl":"https://github.com/asyncapi/nodejs-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"Node.js Websockets Template","description":"Node.js WebSockets template for the AsyncAPI Generator. It showcases how from a single AsyncAPI document you can generate a server and a client at the same time.","links":{"repoUrl":"https://github.com/asyncapi/nodejs-ws-template"},"filters":{"language":[{"name":"JavaScript","color":"bg-[#F2F1C7]","borderColor":"border-[#BFBE86]"}],"technology":[{"name":"Node.js","color":"bg-[#BDFF67]","borderColor":"border-[#84CE24]"}],"categories":["generator-template"],"hasCommercial":false,"isAsyncAPIOwner":true}}]},"Others":{"description":"The following is a list of tools that comes under Other category.","toolsList":[{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/asyncapi/cli"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":true}},{"title":"AsyncAPI CLI","description":"One CLI to rule them all. \nThis is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \nYou can use it to generate docs or code, validate AsyncAPI document and event create new documents.\n","links":{"websiteUrl":"https://www.asyncapi.com/tools/cli","repoUrl":"https://github.com/hkirat/asyncapi-fork"},"filters":{"technology":[{"name":"TypeScript","color":"bg-[#61d0f2]","borderColor":"border-[#40ccf7]"}],"categories":["others","cli"],"hasCommercial":false,"isAsyncAPIOwner":false}}]}} \ No newline at end of file From 12ef0de314066bcc39c308db8f3d111af175ae82 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Wed, 31 Jul 2024 07:36:42 +0200 Subject: [PATCH 09/69] docs(cli): update latest cli documentation (#3128) --- markdown/docs/tools/cli/usage.md | 56 +++++++++++++++++--------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/markdown/docs/tools/cli/usage.md b/markdown/docs/tools/cli/usage.md index 183def2597ea..676d97ed9178 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.1.0 linux-x64 node-v18.20.3 +@asyncapi/cli/2.1.1 linux-x64 node-v18.20.4 $ asyncapi --help [COMMAND] USAGE $ asyncapi COMMAND @@ -99,7 +99,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.1.0/src/commands/bundle.ts)_ +_See code: [src/commands/bundle.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/bundle.ts)_ ## `asyncapi config` @@ -113,7 +113,7 @@ DESCRIPTION CLI config settings ``` -_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/index.ts)_ +_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/index.ts)_ ## `asyncapi config analytics` @@ -133,7 +133,7 @@ DESCRIPTION Enable or disable analytics for metrics collection ``` -_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/analytics.ts)_ +_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/analytics.ts)_ ## `asyncapi config context` @@ -147,7 +147,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.1.0/src/commands/config/context/index.ts)_ +_See code: [src/commands/config/context/index.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/index.ts)_ ## `asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH` @@ -169,7 +169,7 @@ DESCRIPTION Add a context to the store ``` -_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/context/add.ts)_ +_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/add.ts)_ ## `asyncapi config context current` @@ -186,7 +186,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.1.0/src/commands/config/context/current.ts)_ +_See code: [src/commands/config/context/current.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/current.ts)_ ## `asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH` @@ -207,7 +207,7 @@ DESCRIPTION Edit a context in the store ``` -_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/context/edit.ts)_ +_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/edit.ts)_ ## `asyncapi config context init [CONTEXT-FILE-PATH]` @@ -230,7 +230,7 @@ DESCRIPTION Initialize context ``` -_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/context/init.ts)_ +_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/init.ts)_ ## `asyncapi config context list` @@ -247,7 +247,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.1.0/src/commands/config/context/list.ts)_ +_See code: [src/commands/config/context/list.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/list.ts)_ ## `asyncapi config context remove CONTEXT-NAME` @@ -267,7 +267,7 @@ DESCRIPTION Delete a context from the store ``` -_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/context/remove.ts)_ +_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/remove.ts)_ ## `asyncapi config context use CONTEXT-NAME` @@ -287,7 +287,7 @@ DESCRIPTION Set a context as current ``` -_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/context/use.ts)_ +_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/use.ts)_ ## `asyncapi config versions` @@ -304,7 +304,7 @@ DESCRIPTION Show versions of AsyncAPI tools used ``` -_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/config/versions.ts)_ +_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/versions.ts)_ ## `asyncapi convert [SPEC-FILE]` @@ -326,7 +326,7 @@ DESCRIPTION Convert asyncapi documents older to newer versions ``` -_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/convert.ts)_ +_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/convert.ts)_ ## `asyncapi diff OLD NEW` @@ -366,7 +366,7 @@ DESCRIPTION Find diff between two asyncapi files ``` -_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/diff.ts)_ +_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/diff.ts)_ ## `asyncapi generate` @@ -380,7 +380,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.1.0/src/commands/generate/index.ts)_ +_See code: [src/commands/generate/index.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/generate/index.ts)_ ## `asyncapi generate fromTemplate ASYNCAPI TEMPLATE` @@ -424,7 +424,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.1.0/src/commands/generate/fromTemplate.ts)_ +_See code: [src/commands/generate/fromTemplate.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/generate/fromTemplate.ts)_ ## `asyncapi generate models LANGUAGE FILE` @@ -495,7 +495,7 @@ DESCRIPTION Generates typed models ``` -_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/generate/models.ts)_ +_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/generate/models.ts)_ ## `asyncapi new` @@ -553,7 +553,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.1.0/src/commands/new/index.ts)_ +_See code: [src/commands/new/index.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/new/index.ts)_ ## `asyncapi new file` @@ -611,7 +611,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.1.0/src/commands/new/file.ts)_ +_See code: [src/commands/new/file.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/new/file.ts)_ ## `asyncapi new glee` @@ -633,7 +633,7 @@ DESCRIPTION Creates a new Glee project ``` -_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/new/glee.ts)_ +_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/new/glee.ts)_ ## `asyncapi new template` @@ -657,7 +657,7 @@ DESCRIPTION Creates a new template ``` -_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/new/template.ts)_ +_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/new/template.ts)_ ## `asyncapi optimize [SPEC-FILE]` @@ -695,7 +695,7 @@ EXAMPLES $ asyncapi optimize ./asyncapi.yaml --optimization=remove-components --output=terminal --no-tty ``` -_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/optimize.ts)_ +_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/optimize.ts)_ ## `asyncapi start` @@ -704,7 +704,7 @@ USAGE $ asyncapi start ``` -_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/start/index.ts)_ +_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/start/index.ts)_ ## `asyncapi start studio` @@ -723,7 +723,7 @@ DESCRIPTION starts a new local instance of Studio ``` -_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/start/studio.ts)_ +_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/start/studio.ts)_ ## `asyncapi validate [SPEC-FILE]` @@ -732,7 +732,7 @@ validate asyncapi file ``` USAGE $ asyncapi validate [SPEC-FILE] [-h] [-w] [--log-diagnostics] [--diagnostics-format - json|stylish|junit|html|text|teamcity|pretty] [--fail-severity error|warn|info|hint] + json|stylish|junit|html|text|teamcity|pretty] [--fail-severity error|warn|info|hint] [--score] ARGUMENTS SPEC-FILE spec path, url, or context-name @@ -746,10 +746,12 @@ FLAGS code --[no-]log-diagnostics log validation diagnostics or not + --score Compute the score of the AsyncAPI document. Scoring is based on whether the + document has description, license, server and/or channels. DESCRIPTION validate asyncapi file ``` -_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.1.0/src/commands/validate.ts)_ +_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/validate.ts)_ From 4fdcaca3ed13f138e2c572d9331198a4e588fce7 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Wed, 31 Jul 2024 09:21:07 +0200 Subject: [PATCH 10/69] docs(cli): update latest cli documentation (#3129) --- markdown/docs/tools/cli/usage.md | 58 +++++++++++++++++--------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/markdown/docs/tools/cli/usage.md b/markdown/docs/tools/cli/usage.md index 676d97ed9178..bb0a863247f7 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.1.1 linux-x64 node-v18.20.4 +@asyncapi/cli/2.2.0 linux-x64 node-v18.20.4 $ asyncapi --help [COMMAND] USAGE $ asyncapi COMMAND @@ -99,7 +99,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.1.1/src/commands/bundle.ts)_ +_See code: [src/commands/bundle.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/bundle.ts)_ ## `asyncapi config` @@ -113,7 +113,7 @@ DESCRIPTION CLI config settings ``` -_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/index.ts)_ +_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/index.ts)_ ## `asyncapi config analytics` @@ -133,7 +133,7 @@ DESCRIPTION Enable or disable analytics for metrics collection ``` -_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/analytics.ts)_ +_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/analytics.ts)_ ## `asyncapi config context` @@ -147,7 +147,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.1.1/src/commands/config/context/index.ts)_ +_See code: [src/commands/config/context/index.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/index.ts)_ ## `asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH` @@ -169,7 +169,7 @@ DESCRIPTION Add a context to the store ``` -_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/add.ts)_ +_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/add.ts)_ ## `asyncapi config context current` @@ -186,7 +186,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.1.1/src/commands/config/context/current.ts)_ +_See code: [src/commands/config/context/current.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/current.ts)_ ## `asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH` @@ -207,7 +207,7 @@ DESCRIPTION Edit a context in the store ``` -_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/edit.ts)_ +_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/edit.ts)_ ## `asyncapi config context init [CONTEXT-FILE-PATH]` @@ -230,7 +230,7 @@ DESCRIPTION Initialize context ``` -_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/init.ts)_ +_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/init.ts)_ ## `asyncapi config context list` @@ -247,7 +247,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.1.1/src/commands/config/context/list.ts)_ +_See code: [src/commands/config/context/list.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/list.ts)_ ## `asyncapi config context remove CONTEXT-NAME` @@ -267,7 +267,7 @@ DESCRIPTION Delete a context from the store ``` -_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/remove.ts)_ +_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/remove.ts)_ ## `asyncapi config context use CONTEXT-NAME` @@ -287,7 +287,7 @@ DESCRIPTION Set a context as current ``` -_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/context/use.ts)_ +_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/context/use.ts)_ ## `asyncapi config versions` @@ -304,7 +304,7 @@ DESCRIPTION Show versions of AsyncAPI tools used ``` -_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/config/versions.ts)_ +_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/config/versions.ts)_ ## `asyncapi convert [SPEC-FILE]` @@ -326,7 +326,7 @@ DESCRIPTION Convert asyncapi documents older to newer versions ``` -_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/convert.ts)_ +_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/convert.ts)_ ## `asyncapi diff OLD NEW` @@ -366,7 +366,7 @@ DESCRIPTION Find diff between two asyncapi files ``` -_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/diff.ts)_ +_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/diff.ts)_ ## `asyncapi generate` @@ -380,7 +380,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.1.1/src/commands/generate/index.ts)_ +_See code: [src/commands/generate/index.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/generate/index.ts)_ ## `asyncapi generate fromTemplate ASYNCAPI TEMPLATE` @@ -424,7 +424,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.1.1/src/commands/generate/fromTemplate.ts)_ +_See code: [src/commands/generate/fromTemplate.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/generate/fromTemplate.ts)_ ## `asyncapi generate models LANGUAGE FILE` @@ -495,7 +495,7 @@ DESCRIPTION Generates typed models ``` -_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/generate/models.ts)_ +_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/generate/models.ts)_ ## `asyncapi new` @@ -553,7 +553,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.1.1/src/commands/new/index.ts)_ +_See code: [src/commands/new/index.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/new/index.ts)_ ## `asyncapi new file` @@ -611,7 +611,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.1.1/src/commands/new/file.ts)_ +_See code: [src/commands/new/file.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/new/file.ts)_ ## `asyncapi new glee` @@ -633,7 +633,7 @@ DESCRIPTION Creates a new Glee project ``` -_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/new/glee.ts)_ +_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/new/glee.ts)_ ## `asyncapi new template` @@ -657,7 +657,7 @@ DESCRIPTION Creates a new template ``` -_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.1.1/src/commands/new/template.ts)_ +_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.2.0/src/commands/new/template.ts)_ ## `asyncapi optimize [SPEC-FILE]` @@ -666,7 +666,7 @@ optimize asyncapi specification file ``` USAGE $ asyncapi optimize [SPEC-FILE] [-h] [-p - remove-components|reuse-components|move-duplicates-to-components|move-all-to-components...] [-o + remove-components|reuse-components|move-duplicates-to-components|move-all-to-components...] [-i schema...] [-o terminal|new-file|overwrite] [--no-tty] ARGUMENTS @@ -674,6 +674,8 @@ ARGUMENTS FLAGS -h, --help Show CLI help. + -i, --ignore=

diff --git a/components/tools/FiltersDropdown.tsx b/components/tools/FiltersDropdown.tsx index fbf2d5c7a205..beb68e81eb5a 100644 --- a/components/tools/FiltersDropdown.tsx +++ b/components/tools/FiltersDropdown.tsx @@ -2,6 +2,8 @@ import { twMerge } from 'tailwind-merge'; import type { Category, Language, Technology } from '@/types/components/tools/ToolDataType'; +import Checkbox from './Checkbox'; + type DataList = Language[] | Technology[] | Category[]; interface FiltersDropdownProps { @@ -25,7 +27,7 @@ export default function FiltersDropdown({ setCheckedOptions, className = '' }: FiltersDropdownProps) { - const handleClickOption = (event: React.MouseEvent, option: string) => { + const handleClickOption = (option: string) => { const isChecked = checkedOptions.includes(option); const updatedOptions = isChecked ? checkedOptions.filter((item) => item !== option) : [...checkedOptions, option]; @@ -40,22 +42,7 @@ export default function FiltersDropdown({ {dataList.map((data, index) => { const checked = checkedOptions.includes(data.name); - return ( -
handleClickOption(event, data.name)} - > - {checked ? ( - checked - ) : ( - unchecked - )} -
{data.name}
-
- ); + return ; })}
); diff --git a/components/tools/Toggle.stories.tsx b/components/tools/Toggle.stories.tsx new file mode 100644 index 000000000000..2fcaf81c26df --- /dev/null +++ b/components/tools/Toggle.stories.tsx @@ -0,0 +1,42 @@ +import { useArgs } from '@storybook/preview-api'; +import type { Meta, StoryObj } from '@storybook/react'; + +import type { ToggleProps } from '@/types/components/tools/TogglePropsType'; + +import Toggle from './Toggle'; + +const meta: Meta = { + title: 'Components/Toggle', + component: Toggle +}; + +export default meta; + +type Story = StoryObj; + +export const DefaultToggle: Story = { + args: { + checked: true, + label: 'Toggle me!' + }, + + render: (args: ToggleProps) => { + const [{ checked }, updateArgs] = useArgs(); + + const setChecked = () => { + updateArgs({ checked: !checked }); + }; + + return ; + } +}; + +export const ColorfulToggle: Story = { + ...DefaultToggle, + + args: { + ...DefaultToggle.args, + bgColor: 'bg-gray-200', + checkedStateBgColor: 'bg-primary-500' + } +}; diff --git a/components/tools/Toggle.tsx b/components/tools/Toggle.tsx new file mode 100644 index 000000000000..bfa324b8538b --- /dev/null +++ b/components/tools/Toggle.tsx @@ -0,0 +1,33 @@ +import { twMerge } from 'tailwind-merge'; + +import type { ToggleProps } from '@/types/components/tools/TogglePropsType'; + +/** + * Toggle component for displaying and controlling a toggle switch. + */ +const Toggle = ({ + checked, + setChecked, + label, + bgColor = 'bg-gray-200', + checkedStateBgColor = 'bg-secondary-500' +}: ToggleProps) => { + return ( + + ); +}; + +export default Toggle; diff --git a/package-lock.json b/package-lock.json index 879c389d24d7..7c676d6d81df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6700,8 +6700,7 @@ "node_modules/@storybook/global": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", - "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", - "dev": true + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==" }, "node_modules/@storybook/icons": { "version": "1.2.10", @@ -18260,8 +18259,7 @@ "node_modules/map-or-similar": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", - "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", - "dev": true + "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==" }, "node_modules/markdown-extensions": { "version": "2.0.0", @@ -19866,7 +19864,6 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", - "dev": true, "dependencies": { "map-or-similar": "^1.5.0" } @@ -28009,7 +28006,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", "integrity": "sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==", - "dev": true, "dependencies": { "memoizerific": "^1.11.3" } diff --git a/types/components/tools/CheckboxPropsType.ts b/types/components/tools/CheckboxPropsType.ts new file mode 100644 index 000000000000..e535bb41dd4c --- /dev/null +++ b/types/components/tools/CheckboxPropsType.ts @@ -0,0 +1,27 @@ +export interface CheckboxProps { + // eslint-disable-next-line prettier/prettier + + /** The name to be displayed inside the checkbox. */ + name: string; + + /** If the checkbox is checked or not. */ + checked: boolean; + + /** The background color of the checkbox. */ + bgColor?: string; + + /** The text color of the checkbox. */ + textColor?: string; + + /** The border color of the checkbox. */ + borderColor?: string; + + /** The background color of the checkbox when it is checked. */ + checkedStateBgColor?: string; + + /** The text color of the checkbox when it is checked. */ + checkedStateTextColor?: string; + + /** Function to handle the click event of the checkbox. */ + handleClickOption: (name: string) => void; +} diff --git a/types/components/tools/TogglePropsType.ts b/types/components/tools/TogglePropsType.ts new file mode 100644 index 000000000000..5514aeb0f74f --- /dev/null +++ b/types/components/tools/TogglePropsType.ts @@ -0,0 +1,18 @@ +export interface ToggleProps { + // eslint-disable-next-line prettier/prettier + + /** Current state of the toggle. */ + checked: boolean; + + /** Function to update the toggle state. */ + setChecked: React.Dispatch>; + + /** Label text for the toggle. */ + label?: string; + + /** The background color of the checkbox. */ + bgColor?: string; + + /** The background color of the checkbox when it is checked. */ + checkedStateBgColor?: string; +} From 6926ed2d4081ec9afd2073ad37bec8bb9ff7ef64 Mon Sep 17 00:00:00 2001 From: Ashmit JaiSarita Gupta <43639341+devilkiller-ag@users.noreply.github.com> Date: Fri, 30 Aug 2024 10:42:25 +0530 Subject: [PATCH 35/69] feat: created loader component and added stories for it (#3123) Co-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com> --- components/ClickableLogo.tsx | 2 +- components/Loader.stories.tsx | 115 ++++++++++++++++ components/Loader.tsx | 35 +++-- components/NewsletterSubscribe.tsx | 3 +- components/footer/Footer.tsx | 2 +- components/icons/AsyncAPIColorIcon.tsx | 18 +++ components/icons/CircularLoader.tsx | 18 +++ components/icons/Icons.mdx | 135 +++---------------- components/{ => logos}/AsyncAPILogo.tsx | 0 components/{ => logos}/AsyncAPILogoLight.tsx | 0 components/logos/logos.mdx | 26 ++-- components/navigation/MobileNavMenu.tsx | 2 +- components/navigation/NavBar.tsx | 2 +- components/tools/ToolsDashboard.tsx | 8 +- pages/index.tsx | 18 +-- 15 files changed, 229 insertions(+), 155 deletions(-) create mode 100644 components/Loader.stories.tsx create mode 100644 components/icons/AsyncAPIColorIcon.tsx create mode 100644 components/icons/CircularLoader.tsx rename components/{ => logos}/AsyncAPILogo.tsx (100%) rename components/{ => logos}/AsyncAPILogoLight.tsx (100%) diff --git a/components/ClickableLogo.tsx b/components/ClickableLogo.tsx index 0ce7f081e4f3..e40e7fa488c2 100644 --- a/components/ClickableLogo.tsx +++ b/components/ClickableLogo.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; -import AsyncAPILogo from './AsyncAPILogo'; +import AsyncAPILogo from './logos/AsyncAPILogo'; interface IClickableLogoProps { href?: string; diff --git a/components/Loader.stories.tsx b/components/Loader.stories.tsx new file mode 100644 index 000000000000..85d97ee88674 --- /dev/null +++ b/components/Loader.stories.tsx @@ -0,0 +1,115 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import AsyncAPIColorIcon from '@/components/icons/AsyncAPIColorIcon'; +import IconCircularLoader from '@/components/icons/CircularLoader'; + +import Loader from './Loader'; + +const meta: Meta = { + title: 'Components/Loader', + component: Loader +}; + +export default meta; + +type Story = StoryObj; + +const DarkBackgroundDecorator = (Story: any) => ( +
+ +
+); + +export const TextLoader: Story = { + args: { + loaderText: 'Loading...' + } +}; + +export const TextLoaderInDark: Story = { + decorators: [DarkBackgroundDecorator], + args: { + loaderText: 'Loading...', + dark: true + } +}; + +export const PulsatingTextLoader: Story = { + args: { + loaderText: 'Loading...', + pulsating: true + } +}; + +export const PulsatingTextLoaderInDark: Story = { + decorators: [DarkBackgroundDecorator], + args: { + loaderText: 'Loading...', + dark: true, + pulsating: true + } +}; + +export const CircularAnimationLoader: Story = { + args: { + loaderIcon: + } +}; + +export const CircularAnimationLoaderInDark: Story = { + decorators: [DarkBackgroundDecorator], + args: { + loaderIcon: , + dark: true + } +}; + +export const PulsatingIconLoader: Story = { + args: { + loaderIcon: , + pulsating: true + } +}; + +export const PulsatingIconLoaderInDark: Story = { + decorators: [DarkBackgroundDecorator], + args: { + loaderIcon: , + dark: true, + pulsating: true + } +}; + +export const CircularAnimationTextLoader: Story = { + args: { + loaderIcon: , + loaderText: 'Loading...' + } +}; + +export const CircularAnimationTextLoaderInDark: Story = { + decorators: [DarkBackgroundDecorator], + args: { + loaderIcon: , + loaderText: 'Loading...', + dark: true + } +}; + +export const PulsatingIconTextLoader: Story = { + args: { + loaderIcon: , + loaderText: 'Loading...', + pulsating: true + } +}; + +export const PulsatingIconTextLoaderInDark: Story = { + decorators: [DarkBackgroundDecorator], + args: { + loaderIcon: , + loaderText: 'Loading...', + dark: true, + pulsating: true + } +}; diff --git a/components/Loader.tsx b/components/Loader.tsx index 09324174ec2d..94fd29417659 100644 --- a/components/Loader.tsx +++ b/components/Loader.tsx @@ -1,24 +1,39 @@ +import React from 'react'; import { twMerge } from 'tailwind-merge'; interface LoaderProps { + // eslint-disable-next-line prettier/prettier + + /** The text to be displayed along with the loading animation. */ + loaderText?: string; + + /** The icon to be displayed along with the loading animation. */ + loaderIcon?: React.ReactElement | null; + + /** Additional classes for the loader. */ className?: string; + + /** Whether the loader should be in dark mode. */ dark?: boolean; + + /** Whether the loader should be pulsating. */ + pulsating?: boolean; } /** * This component displays a loader. - * @param {LoaderProps} props - The props for the Loader component - * @param {string} props.className - Additional classes for the loader - * @param {boolean} props.dark - Whether the loader should be in dark mode */ -export default function Loader({ className = '', dark = false }: LoaderProps) { +export default function Loader({ + loaderText = '', + loaderIcon = null, + className = '', + dark = false, + pulsating = false +}: LoaderProps) { return ( -
- -
Waiting for response...
+
+ {loaderIcon} +
{loaderText}
); } diff --git a/components/NewsletterSubscribe.tsx b/components/NewsletterSubscribe.tsx index 80ce7474a19d..db7b0a6407df 100644 --- a/components/NewsletterSubscribe.tsx +++ b/components/NewsletterSubscribe.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; +import IconCircularLoader from '@/components/icons/CircularLoader'; import { ButtonType } from '@/types/components/buttons/ButtonPropsType'; import { InputTypes } from '@/types/components/InputBoxPropsType'; import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; @@ -127,7 +128,7 @@ export default function NewsletterSubscribe({ {subtitle} {status === 'loading' ? ( - + } dark={dark} /> ) : ( ; +} diff --git a/components/icons/CircularLoader.tsx b/components/icons/CircularLoader.tsx new file mode 100644 index 000000000000..7cca8dfd8fe9 --- /dev/null +++ b/components/icons/CircularLoader.tsx @@ -0,0 +1,18 @@ +interface CircularLoaderProps { + // eslint-disable-next-line prettier/prettier + + /** Whether the loader should be in dark mode. */ + dark?: boolean; +} + +/** + * CircularLoader Icon + */ +export default function IconCircularLoader({ dark = false }: CircularLoaderProps) { + return ( + + ); +} diff --git a/components/icons/Icons.mdx b/components/icons/Icons.mdx index 7ae712388093..cb6496377a38 100644 --- a/components/icons/Icons.mdx +++ b/components/icons/Icons.mdx @@ -2,6 +2,7 @@ import { Meta, Title, IconGallery, IconItem } from '@storybook/blocks'; import IconAmbassador from './Ambassador'; import IconAsyncAPI from './AsyncAPI'; +import AsyncAPIColorIcon from './AsyncAPIColorIcon'; import IconArrowUp from './ArrowUp'; import IconArrowDown from './ArrowDown'; import IconArrowLeft from './ArrowLeft'; @@ -60,6 +61,7 @@ import IconUsers from './Users'; import Webinar from './Webinar'; import IconYoutube from './YouTube'; import IconYoutubeGray from './YouTubeGray'; +import IconCircularLoader from './CircularLoader'; @@ -70,31 +72,29 @@ These are the icons used in the AsyncAPI website. ## AsyncAPI Icons - - - + + + -{' '} + + + -{' '} - -{' '} - - - - + + + ## Social Media Icons @@ -104,50 +104,34 @@ These are the icons used in the AsyncAPI website. -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - @@ -164,265 +148,184 @@ These are the icons used in the AsyncAPI website. -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - -{' '} - - - - + + + + + + + + diff --git a/components/AsyncAPILogo.tsx b/components/logos/AsyncAPILogo.tsx similarity index 100% rename from components/AsyncAPILogo.tsx rename to components/logos/AsyncAPILogo.tsx diff --git a/components/AsyncAPILogoLight.tsx b/components/logos/AsyncAPILogoLight.tsx similarity index 100% rename from components/AsyncAPILogoLight.tsx rename to components/logos/AsyncAPILogoLight.tsx diff --git a/components/logos/logos.mdx b/components/logos/logos.mdx index c863c4eb059b..06e65b1c46ca 100644 --- a/components/logos/logos.mdx +++ b/components/logos/logos.mdx @@ -5,6 +5,8 @@ import AxwayLogo from './Axway'; import SlackLogo from './Slack'; import AdidasLogo from './Adidas'; import SalesforceLogo from './Salesforce'; +import AsyncAPILogo from './AsyncAPILogo'; +import AsyncAPILogoLight from './AsyncAPILogoLight'; @@ -13,29 +15,31 @@ import SalesforceLogo from './Salesforce'; These are the logos of various companies used in the AsyncAPI website. - - - + + + + + + + -{' '} + + + -{' '} - -{' '} - - - - + + + diff --git a/components/navigation/MobileNavMenu.tsx b/components/navigation/MobileNavMenu.tsx index a108cdaf9a2d..1c6f57e130f9 100644 --- a/components/navigation/MobileNavMenu.tsx +++ b/components/navigation/MobileNavMenu.tsx @@ -2,9 +2,9 @@ import Link from 'next/link'; import { useState } from 'react'; import { SearchButton } from '../AlgoliaSearch'; -import AsyncAPILogo from '../AsyncAPILogo'; import NavItemDropdown from '../icons/NavItemDropdown'; import SearchIcon from '../icons/SearchIcon'; +import AsyncAPILogo from '../logos/AsyncAPILogo'; import communityItems from './communityItems'; import learningItems from './learningItems'; import MenuBlocks from './MenuBlocks'; diff --git a/components/navigation/NavBar.tsx b/components/navigation/NavBar.tsx index 56cda97c5c06..ae8371702033 100644 --- a/components/navigation/NavBar.tsx +++ b/components/navigation/NavBar.tsx @@ -6,12 +6,12 @@ import { useEffect, useState } from 'react'; import { defaultLanguage, languages, useTranslation } from '../../utils/i18n'; import i18nPaths from '../../utils/i18nPaths'; import { SearchButton } from '../AlgoliaSearch'; -import AsyncAPILogo from '../AsyncAPILogo'; import GithubButton from '../buttons/GithubButton'; import { isMobileDevice } from '../helpers/is-mobile'; import { useOutsideClick } from '../helpers/use-outside-click'; import IconLoupe from '../icons/Loupe'; import LanguageSelect from '../languageSelector/LanguageSelect'; +import AsyncAPILogo from '../logos/AsyncAPILogo'; import CommunityPanel from './CommunityPanel'; import LearningPanel from './LearningPanel'; import MobileNavMenu from './MobileNavMenu'; diff --git a/components/tools/ToolsDashboard.tsx b/components/tools/ToolsDashboard.tsx index da74ade22edb..bec91768c97d 100644 --- a/components/tools/ToolsDashboard.tsx +++ b/components/tools/ToolsDashboard.tsx @@ -1,6 +1,7 @@ import { useRouter } from 'next/router'; import { useContext, useEffect, useRef, useState } from 'react'; +import AsyncAPIColorIcon from '@/components/icons/AsyncAPIColorIcon'; import type { ToolsListData } from '@/types/components/tools/ToolDataType'; import ToolsDataList from '../../config/tools.json'; @@ -9,6 +10,7 @@ import ArrowDown from '../icons/ArrowDown'; import Cross from '../icons/Cross'; import FilterIcon from '../icons/Filter'; import SearchIcon from '../icons/Search'; +import Loader from '../Loader'; import CategoryDropdown from './CategoryDropdown'; import Filters from './Filters'; import ToolsList from './ToolsList'; @@ -20,7 +22,6 @@ const ToolsData = ToolsDataList as ToolsListData; */ export default function ToolsDashboard() { const router = useRouter(); - const loader = 'img/loaders/loader.png'; // preloader image for the tools const [loading, setLoading] = useState(false); // used to handle the preloader on the page const filterRef = useRef(); // used to provide ref to the Filter menu and outside click close feature @@ -226,10 +227,7 @@ export default function ToolsDashboard() {
)} {loading ? ( -
- loading -
Loading Tools...
-
+ } pulsating /> ) : (
{checkToolsList ? ( diff --git a/pages/index.tsx b/pages/index.tsx index 348311a7aa2a..f04a906e5ea5 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,23 +1,25 @@ +import Head from '@/components/Head'; +import AsyncAPIColorIcon from '@/components/icons/AsyncAPIColorIcon'; +import Loader from '@/components/Loader'; import { languageDetection } from '@/utils/i18n'; -import Head from '../components/Head'; - /** * @description This is the home page which is the first page that loads when the user visits the website. */ export default function HomePage() { - const loader: string = 'img/loaders/loader.png'; // preloader image for the tools - languageDetection(); return ( <>
-
- Loading... -
Loading...
-
+ } + className='my-60' + dark={false} + pulsating + />
); From abac53eda4e7dcbcb37e68edaea653ae22569f88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 07:12:29 +0200 Subject: [PATCH 36/69] chore(deps): bump webpack from 5.92.1 to 5.94.0 (#3175) --- package-lock.json | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c676d6d81df..14183711e3d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6700,7 +6700,8 @@ "node_modules/@storybook/global": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", - "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==" + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", + "dev": true }, "node_modules/@storybook/icons": { "version": "1.2.10", @@ -7557,20 +7558,14 @@ "version": "8.56.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -12140,9 +12135,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -18259,7 +18254,8 @@ "node_modules/map-or-similar": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", - "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==" + "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", + "dev": true }, "node_modules/markdown-extensions": { "version": "2.0.0", @@ -19864,6 +19860,7 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", + "dev": true, "dependencies": { "map-or-similar": "^1.5.0" } @@ -28006,6 +28003,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", "integrity": "sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==", + "dev": true, "dependencies": { "memoizerific": "^1.11.3" } @@ -29889,11 +29887,10 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -29902,7 +29899,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", From b28b29b391f9a2f5a6e4387ce4c879ba69987209 Mon Sep 17 00:00:00 2001 From: Ashmit JaiSarita Gupta <43639341+devilkiller-ag@users.noreply.github.com> Date: Fri, 30 Aug 2024 10:47:00 +0530 Subject: [PATCH 37/69] feat: configure storybook theme (#3152) Co-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com>%0ACo-authored-by: Ansh Goyal --- .storybook/AsyncAPIStorybookTheme.ts | 42 ++++++++++ .storybook/manager.ts | 6 ++ .storybook/preview.tsx | 2 + public/img/logos/asyncapi-horizontal-logo.svg | 82 +++++++++++++++++++ .../logos/asyncapi-horizontal-white-logo.svg | 82 +++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 .storybook/AsyncAPIStorybookTheme.ts create mode 100644 .storybook/manager.ts create mode 100644 public/img/logos/asyncapi-horizontal-logo.svg create mode 100644 public/img/logos/asyncapi-horizontal-white-logo.svg diff --git a/.storybook/AsyncAPIStorybookTheme.ts b/.storybook/AsyncAPIStorybookTheme.ts new file mode 100644 index 000000000000..9213d73cd955 --- /dev/null +++ b/.storybook/AsyncAPIStorybookTheme.ts @@ -0,0 +1,42 @@ +import { create } from '@storybook/theming/create'; + +export default create({ + // Brand Information + brandTitle: 'AsyncAPI Initiative', + brandUrl: 'https://www.asyncapi.com/', + brandImage: 'img/logos/asyncapi-horizontal-white-logo.svg', + brandTarget: '_blank', + + // Typography + fontBase: '"Work Sans", sans-serif', + fontCode: 'monospace', + + + // Themes + base: 'dark', + + /* -- FULL THEME IS NOT BEING USED DUE TO LACK OF STORYBOOK SUPPORT FOR CUSTOMIZING THE SETTINGS & ACTIONS BAR BG/TEXTCOLOR INDEPENDENTLY. -- + colorPrimary: '#47BCEE', + colorSecondary: '#8851FB', + + // UI + appBg: '#1b1130', + appContentBg: '#ffffff', + appPreviewBg: '#ffffff', + appBorderColor: '#dfe6ea', + appBorderRadius: 4, + + // Text colors + textColor: '#ffffff', + textInverseColor: '#ffffff', + textMutedColor: '#5c6870', + inputTextColor: '#2e3438', + + // Toolbar + barTextColor: '#9E9E9E', + + // Toolbar default and active colors + booleanBg: '#dfe6ea', + booleanSelectedBg: '#8851FB' + */ +}); diff --git a/.storybook/manager.ts b/.storybook/manager.ts new file mode 100644 index 000000000000..cc32cf0e2e41 --- /dev/null +++ b/.storybook/manager.ts @@ -0,0 +1,6 @@ +import { addons } from '@storybook/manager-api'; +import AsyncAPIStorybookTheme from './AsyncAPIStorybookTheme'; + +addons.setConfig({ + theme: AsyncAPIStorybookTheme, +}); diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index c8e7c3b7f91b..29ac0d62998b 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,6 +1,7 @@ import React from "react"; import "../styles/globals.css"; import type { Preview } from "@storybook/react"; +import { themes } from '@storybook/theming'; import { Title, Subtitle, @@ -20,6 +21,7 @@ const preview: Preview = { }, }, docs: { + theme: themes.light, toc: { title: 'Table of contents', }, diff --git a/public/img/logos/asyncapi-horizontal-logo.svg b/public/img/logos/asyncapi-horizontal-logo.svg new file mode 100644 index 000000000000..6f0fc27579e9 --- /dev/null +++ b/public/img/logos/asyncapi-horizontal-logo.svg @@ -0,0 +1,82 @@ + + AsyncAPI Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/logos/asyncapi-horizontal-white-logo.svg b/public/img/logos/asyncapi-horizontal-white-logo.svg new file mode 100644 index 000000000000..2a9e5c4c7167 --- /dev/null +++ b/public/img/logos/asyncapi-horizontal-white-logo.svg @@ -0,0 +1,82 @@ + + AsyncAPI Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0ff94e79fe2704d82ac00dfca1b3e50ff48bc205 Mon Sep 17 00:00:00 2001 From: Saumya Shah <115284013+Saumya40-codes@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:11:58 +0530 Subject: [PATCH 38/69] fix: blog/websocket-part2 having rendering issues (#3163) Co-authored-by: V Thulisile Sibanda <66913810+thulieblack@users.noreply.github.com>%0ACo-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com>%0ACo-authored-by: asyncapi-bot --- markdown/blog/websocket-part2.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/markdown/blog/websocket-part2.md b/markdown/blog/websocket-part2.md index 4098bebd799a..3014381da1e5 100644 --- a/markdown/blog/websocket-part2.md +++ b/markdown/blog/websocket-part2.md @@ -147,7 +147,7 @@ Create two AsyncAPI documents. Treat those two servers as separate services that You can use AsyncAPI also to describe the security of your API. You can describe in a machine-readable way the security mechanism that protects the server. Several [security schemes](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#securitySchemeObject) are supported. In Kraken's case, I could not figure out what kind of security scheme they use from their docs. They seem to have a non-standard set up for getting the authorization token, which is why the only option was to put a human-readable-only description there. -```yaml +~~~yaml servers: public: url: ws.kraken.com @@ -166,16 +166,16 @@ servers: The resulting token must be provided in the "token" field of any new private WebSocket feed subscription: ``` - \{ + { "event": "subscribe", "subscription": - \{ + { "name": "ownTrades", "token": "WW91ciBhdXRoZW50aWNhdGlvbiB0b2tlbiBnb2VzIGhlcmUu" } } ``` -``` +~~~ ### Endpoints aka Channels @@ -436,7 +436,7 @@ For **automation** road described in section [Choosing the right road to Rome](# > You can open this document directly in AsyncAPI Studio by clicking [this](https://studio.asyncapi.com?url=https://gist.githubusercontent.com/derberg/4e419d6ff5870c7c3f5f443e8bd30535/raw/5e9b733b80a0209ba5520e5f41ab18c2a112e0a9/asyncapi-websocket-kraken.yml) link. Compare it also with the [original documentation](https://docs.kraken.com/websockets/). -```yml +~~~yaml asyncapi: 2.0.0 info: @@ -474,10 +474,10 @@ servers: The resulting token must be provided in the "token" field of any new private WebSocket feed subscription: ``` - \{ + { "event": "subscribe", "subscription": - \{ + { "name": "ownTrades", "token": "WW91ciBhdXRoZW50aWNhdGlvbiB0b2tlbiBnb2VzIGhlcmUu" } @@ -809,6 +809,6 @@ components: type: string description: Format of each pair is "A/B", where A and B are ISO 4217-A3 for standardized assets and popular unique symbol if not standardized. pattern: '[A-Z\s]+\/[A-Z\s]+' -``` +~~~ Stay tuned for more articles around WebSocket and AsyncAPI. Share your feedback and connect with the AsyncAPI community in our [Slack workspace](https://www.asyncapi.com/slack-invite/). From 6e15882a6868445e18712b1061bb4c2c5f0bae4d Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Fri, 30 Aug 2024 11:51:07 +0200 Subject: [PATCH 39/69] chore: introduce new contibutor guide with triagers and committers (#2959) Co-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com>%0ACo-authored-by: Quetzalli %0ACo-authored-by: Ansh Goyal %0ACo-authored-by: asyncapi-bot --- CONTRIBUTING.md | 93 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1334921c2813..6a20e1c3395f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,20 @@ -# Contributing to AsyncAPI -We love your input! We want to make contributing to this project as easy and transparent as possible. +# Welcome to AsyncAPI -## Contribution recogniton +Welcome to our open-source project! We're excited to have you join our community and contribute to making our project better. Please follow this guide to ensure a smooth contribution process and maintain the quality of our codebase and documentation. -We use [All Contributors](https://allcontributors.org/docs/en/specification) specification to handle recognitions. For more details read [this](https://github.com/asyncapi/community/blob/master/recognize-contributors.md) document. +We encourage all contributors to familiarize themselves with these guidelines and actively participate in the project's growth. If you have any questions or need assistance, don't hesitate to contact the project maintainers or community members. -## Summary of the contribution flow +## Code of Conduct + +AsyncAPI has adopted a Code of Conduct (CoC) that we expect project participants to adhere to. Please [read the full CoC text](./CODE_OF_CONDUCT.md) to understand the expected behavior. + +## Our Development Process + +We use Github to host code, track issues, feature requests, and accept pull requests. + +## Contribution flow -The following is a summary of the ideal contribution flow. Please, note that Pull Requests can also be rejected by the maintainers when appropriate. +The following is a summary of the ideal contribution flow. ``` ┌───────────────────────┐ @@ -36,44 +43,70 @@ The following is a summary of the ideal contribution flow. Please, note that Pul └───────────────────────┘ ``` -## Code of Conduct -AsyncAPI has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](./CODE_OF_CONDUCT.md) so that you can understand what sort of behaviour is expected. +Issues and pull requests without activity from the creator within 14 days will be automatically closed by a triager or committer. However, closure does not mean rejection. If you wish to revisit a closed issue or pull a request, open a new one referencing the closed item. -## Our Development Process -We use Github to host code, to track issues and feature requests, as well as accept pull requests. +Issues and pull requests without activity from the triager or committer within 14 days may occur for many reasons. The creator may use the `/ptal` comment in the pull request to call out maintainers. -## Issues -[Open an issue](https://github.com/asyncapi/asyncapi/issues/new) **only** if you want to report a bug or a feature. Don't open issues for questions or support, instead join our [Slack workspace](https://www.asyncapi.com/slack-invite) and ask there. Don't forget to follow our [Slack Etiquette](https://github.com/asyncapi/community/blob/master/slack-etiquette.md) while interacting with community members! It's more likely you'll get help, and much faster! +### Issues -## Bug Reports and Feature Requests +[Open an issue](https://github.com/asyncapi/asyncapi/issues/new) **only** if you want to report a bug or a feature. Don't open issues for questions or support; join our [AsyncAPI Slack workspace](https://www.asyncapi.com/slack-invite) and post your queries on the relevant channels. Don't forget to follow our [Slack Etiquette](https://github.com/asyncapi/community/blob/master/slack-etiquette.md) while interacting with community members! It's more likely you'll get help, and much faster! -Please use our issues templates that provide you with hints on what information we need from you to help you out. +### Bug Reports and Feature Requests -## Pull Requests +Please use our issues templates, which provide hints on what information we need from you to help you out. -**Please, make sure you open an issue before starting with a Pull Request, unless it's a typo or a really obvious error.** Pull requests are the best way to propose changes to the specification. Get familiar with our document that explains [Git workflow](https://github.com/asyncapi/community/blob/master/git-workflow.md) used in our repositories. +### Pull Requests -## Conventional commits +**Please open an issue before starting a Pull Request unless it's a typo or a really obvious error.** Pull requests are the best way to propose changes to the specification. It may be rejected if no issue was created first to discuss the need for a pull request. -Our repositories follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) specification. Releasing to GitHub and NPM is done with the support of [semantic-release](https://semantic-release.gitbook.io/semantic-release/). +Get familiar with our document that explains the [Git workflow](https://github.com/asyncapi/community/blob/master/git-workflow.md) used in our repositories. -Pull requests should have a title that follows the specification, otherwise, merging is blocked. If you are not familiar with the specification simply ask maintainers to modify. You can also use this cheatsheet if you want: +## Conventional commits + +Our repositories follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) specification. -- `fix: ` prefix in the title indicates that PR is a bug fix and PATCH release must be triggered. -- `feat: ` prefix in the title indicates that PR is a feature and MINOR release must be triggered. -- `docs: ` prefix in the title indicates that PR is only related to the documentation and there is no need to trigger release. -- `chore: ` prefix in the title indicates that PR is only related to cleanup in the project and there is no need to trigger release. -- `test: ` prefix in the title indicates that PR is only related to tests and there is no need to trigger release. -- `refactor: ` prefix in the title indicates that PR is only related to refactoring and there is no need to trigger release. +Pull requests should have a title that follows the specification; otherwise, merging is blocked. If you are unfamiliar with the specification, ask maintainers to modify it. You can also use this cheatsheet if you want: -What about MAJOR release? just add `!` to the prefix, like `fix!: ` or `refactor!: ` +- `fix: ` prefix in the title indicates that the PR is a bug fix. +- `feat: ` prefix in the title indicates that the PR is a feature. +- `docs: ` prefix in the title indicates that the PR only relates to the documentation. +- `chore: ` prefix in the title indicates that the PR is only related to cleanup in the project. +- `test: ` prefix in the title indicates that the PR is only related to tests. +- `refactor: ` prefix in the title indicates that the PR is only related to refactoring. -Prefix that follows specification is not enough though. Remember that the title must be clear and descriptive with usage of [imperative mood](https://chris.beams.io/posts/git-commit/#imperative). +A prefix that follows specification is not enough. Remember that the title must be clear and descriptive, using the [imperative mood](https://chris.beams.io/posts/git-commit/#imperative). -Happy contributing :heart: +Happy contributing! :heart: ## License + When you submit changes, your submissions are understood to be under the same [Apache 2.0 License](https://github.com/asyncapi/asyncapi/blob/master/LICENSE) that covers the project. Feel free to [contact the maintainers](https://www.asyncapi.com/slack-invite) if that's a concern. -## References -This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/master/CONTRIBUTING.md). \ No newline at end of file +## Contribution recognition + +We use the [All Contributors](https://allcontributors.org/docs/en/specification) specification to handle recognitions. Read the [`recognize contributors` document](https://github.com/asyncapi/community/blob/master/recognize-contributors.md). + +## Maintainers setup + +To streamline project management and facilitate onboarding, the maintainer's setup includes two roles: `triager` and `committer`. + +There are also two separate areas of responsibility: docs and code. A committer can be responsible for code only, and a triager can be responsible for docs only. A maintainer can hold all roles and participate in different areas. + +We recognize that because of the project's size and complexity, areas of responsibility can also become more granular. For example, a committer can be responsible for docs, but only community docs or a committer can be responsible only for project design. Project maintainers assess and approve these exceptions. + +### Triager + +Triagers are responsible for labeling, commenting, and managing issues and pull requests. + +- Triagers assess newly-opened issues and pull requests. +Responsibilities include labeling issues and pull requests, commenting, closing, and reopening items as needed, as well as assisting users and novice contributors. +- Triagers are crucial in enforcing the contributor guide and maintaining a clean backlog. +If a triager plans to become a committer, they should consult existing committers to gradually gain more rights. It's crucial to earn the trust of existing committers so they feel confident in your ability to merge PRs. A triager should consistently demonstrate dedication by regularly fulfilling their duties and actively reviewing PRs, providing code/docs suggestions and recommendations. This shows the committers that the triager is knowledgeable about the docs/codebase and committed to maintaining its quality. + +### Committer + +Committers are responsible for technical oversight, pull request approval, and onboarding of new maintainers. + +- Committers approve pull requests and oversee the technical direction of the project. +- They are responsible for reviewing and approving pull requests for merging. +- Committers also play a role in onboarding new maintainers and triagers. From b19618f739a95f2695e1901613aa9634beed698b Mon Sep 17 00:00:00 2001 From: Mike Scriven Date: Fri, 30 Aug 2024 16:32:44 +0100 Subject: [PATCH 40/69] docs: fix typos in add-server.md (#3171) --- markdown/docs/concepts/asyncapi-document/add-server.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/markdown/docs/concepts/asyncapi-document/add-server.md b/markdown/docs/concepts/asyncapi-document/add-server.md index 4032f00ddb58..5dff23779638 100644 --- a/markdown/docs/concepts/asyncapi-document/add-server.md +++ b/markdown/docs/concepts/asyncapi-document/add-server.md @@ -46,10 +46,10 @@ Here's an example of an AsyncAPI document with two servers referenced from the ` ```yaml servers: kafka-test: - $ref: '#/components/servers/kafka-test + $ref: '#/components/servers/kafka-test' mqtt-test: - $ref: '#/components/servers/mqtt-test -components + $ref: '#/components/servers/mqtt-test' +components: servers: kafka-test: host: my.kafka.pl From 6b541507f7a7ff0ec6a1178d4ec8845169a4c0a5 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Sat, 31 Aug 2024 18:59:56 +0200 Subject: [PATCH 41/69] docs(cli): update latest cli documentation (#3178) --- markdown/docs/tools/cli/context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markdown/docs/tools/cli/context.md b/markdown/docs/tools/cli/context.md index 95fbcc8e0083..30803782e556 100644 --- a/markdown/docs/tools/cli/context.md +++ b/markdown/docs/tools/cli/context.md @@ -123,7 +123,7 @@ Map of filesystem paths to target AsyncAPI documents. Field Pattern | Type | Description ---|:---:|--- -{contextName} | `string` | An optional string value representing filesystem path to the target AsyncAPI document. +\{contextName\} | `string` | An optional string value representing filesystem path to the target AsyncAPI document. ### Minimal Empty Context File Raw JSON: From 0bd048c08948341a6df5b5bdf7bdf854ec9d4b7e Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Sun, 1 Sep 2024 12:10:28 +0200 Subject: [PATCH 42/69] docs(community): update latest maintainers list (#3182) --- config/MAINTAINERS.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config/MAINTAINERS.json b/config/MAINTAINERS.json index d34e97f45b82..1d9a5cab3e3d 100644 --- a/config/MAINTAINERS.json +++ b/config/MAINTAINERS.json @@ -963,5 +963,14 @@ "learning-paths" ], "githubID": 77982319 + }, + { + "name": "Lorenz Simon", + "github": "lorenzsimon", + "isTscMember": false, + "repos": [ + "kotlin-asyncapi" + ], + "githubID": 39913716 } ] \ No newline at end of file From 025f24d0802a88bc0ae238f0698c3440ad11d421 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Mon, 2 Sep 2024 18:17:59 +0200 Subject: [PATCH 43/69] docs(community): update latest tsc members list (#3184) --- config/AMBASSADORS_MEMBERS.json | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/config/AMBASSADORS_MEMBERS.json b/config/AMBASSADORS_MEMBERS.json index 1eef57473b15..c86c4cca9898 100644 --- a/config/AMBASSADORS_MEMBERS.json +++ b/config/AMBASSADORS_MEMBERS.json @@ -686,5 +686,45 @@ "link": "https://www.asyncapi.com/casestudies/hdiglobal" } ] + }, + { + "name": "Lorna Mitchell", + "bio": "Lorna is based in Yorkshire, UK; she is a technology leader and expert in developer experience, passionate about enhancing APIs and developer tools. In her day job as VP of Developer Experience at Redocly, she works on API and documentation tools for technical teams. Lorna is a published author and a regular speaker at conferences, sharing her insights on a variety of tech-related topics. Lorna serves on the OpenUK board, is on the Technical Steering Committee for OpenAPI specification, and maintains open source projects. To learn more about Lorna's activities, visit her website at https://lornajane.net.", + "title": "APIs and Open Source", + "img": "https://lornajane.net/wp-content/uploads/2011/08/IMG_9410-smaller.jpg", + "github": "lornajane", + "twitter": "lornajane", + "linkedin": "lornajane", + "company": "Redocly", + "country": "UK", + "contributions": [ + { + "type": "presentation", + "title": "API Governance for AsyncAPI", + "date": { + "year": 2023, + "month": "September" + }, + "link": "https://noti.st/lornajane/aOuXwe/api-governance-for-asyncapi" + }, + { + "type": "presentation", + "title": "AsyncAPI for Apache Kafka", + "date": { + "year": 2021, + "month": "May" + }, + "link": "https://videos.confluent.io/watch/hzjXP8QmLtYRNPukNFUu2D?" + }, + { + "type": "article", + "title": "Lint AsyncAPI with Redocly CLI", + "date": { + "year": 2023, + "month": "August" + }, + "link": "https://redocly.com/docs/cli/guides/lint-asyncapi" + } + ] } ] From d4a6001eec398020f68670ddc33dc97a02766989 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Tue, 3 Sep 2024 08:19:05 +0200 Subject: [PATCH 44/69] docs(glee): update latest glee documentation (#3188) --- markdown/docs/tools/glee/authentication.md | 97 ++++---- markdown/docs/tools/glee/bearerToken.md | 43 ++-- .../glee/crypto-websockets-interactive.md | 2 +- markdown/docs/tools/glee/env-vars-config.md | 213 +++++++----------- .../tools/glee/function-lifecycle-events.md | 100 ++++---- markdown/docs/tools/glee/glee-auth-intro.md | 9 +- markdown/docs/tools/glee/glee-template.md | 104 --------- markdown/docs/tools/glee/httpApiKey.md | 36 ++- markdown/docs/tools/glee/index.md | 131 +---------- markdown/docs/tools/glee/installation.md | 86 ++++--- .../docs/tools/glee/your-first-glee-app.md | 125 ++++++++++ 11 files changed, 404 insertions(+), 542 deletions(-) delete mode 100644 markdown/docs/tools/glee/glee-template.md create mode 100644 markdown/docs/tools/glee/your-first-glee-app.md diff --git a/markdown/docs/tools/glee/authentication.md b/markdown/docs/tools/glee/authentication.md index 841bbfca78c4..031810402e64 100644 --- a/markdown/docs/tools/glee/authentication.md +++ b/markdown/docs/tools/glee/authentication.md @@ -1,45 +1,44 @@ --- -title: 'Authentication functions' +title: 'Authentication Functions' weight: 70 --- -# Getting started with Authentication functions +# Getting Started with Authentication Functions -Authentication in Glee can be done using authentication functions. Authentication functions are files that export either one or both of the `clientAuth` and `serverAuth` Node.js functions: +Authentication in Glee can be implemented using authentication functions. These functions are files that export one or both of the following Node.js functions: `clientAuth` and `serverAuth`: ```js /* websocket.js */ export async function serverAuth({ authProps, done }) { - //server auth logic + // Server authentication logic } export async function clientAuth({ parsedAsyncAPI, serverName }) { - //client auth logic + // Client authentication logic } ``` -Glee looks for authentication files in the `auth` directory by default but it can be configured using [glee config file](env-vars-config). -The name of the authentication file should be the name of the targeted server that the authentication logic should work for. +Glee searches for authentication files in the `auth` directory by default. However, this can be configured using the [glee config file](env-vars-config). The authentication file's name should match the targeted server for which the authentication logic is intended. -## Supported Authentication Values in asyncapi.yaml file +## Supported Authentication Values in the asyncapi.yaml File -AsyncAPI currently supports a variety of authentication formats as specified in the [documentation](https://www.asyncapi.com/docs/reference/specification/v3.0.0#securitySchemeObject), however Glee supports the following authentication schemas. +AsyncAPI supports a variety of authentication formats as specified in its [documentation](https://www.asyncapi.com/docs/reference/specification/v3.0.0#securitySchemeObject). Glee, however, supports the following authentication schemas: - userPassword - http ("bearer") - httpApiKey - Oauth2 -A sample `asyncapi.yaml` for a **server** with security requirements and a `userPassword` security schemes is shown below: +Below is an example of a `asyncapi.yaml` file for a **server** with security requirements and a `userPassword` security scheme: ```yaml -##server asyncAPI schema +## Server AsyncAPI Schema asyncapi: 3.0.0 info: - title: AsyncAPI IMDB server + title: AsyncAPI IMDB Server version: 1.0.0 - description: This app is a dummy server that would stream the trending/upcoming anime. + description: This app is a dummy server that streams trending/upcoming anime. servers: trendingAnimeServer: host: 'localhost:8081' @@ -53,13 +52,12 @@ components: securitySchemes: userPass: type: userPassword - ``` -A sample `asyncapi.yaml` for a **client** that implements some of the requirements of the server above is as follows: +Here's an example for a **client** that implements some requirements of the server mentioned above: ```yaml -##client asyncAPI schema +## Client AsyncAPI Schema servers: trendingAnime: host: localhost:8081 @@ -78,29 +76,28 @@ components: securitySchemes: userPass: type: userPassword - ``` -Glee can act as both a server and a client. So the need for `serverAuth` and `clientAuth`. Glee acts as a client when the server name is included in the `x-remoteServers` property in the `asyncapi.yaml` file. +Glee can function as both a server and a client. Hence, the need for both `serverAuth` and `clientAuth` functions arises. Glee acts as a client when the server name is included in the `x-remoteServers` property in the `asyncapi.yaml` file. -When Glee acts as a client, it can connect to a Glee server, and when Glee acts as a server it accepts connections from other Glee clients. Hence a Glee application can both accept connections from clients while also sending requests to other Glee applications (servers) at the same time. +When Glee operates as a client, it can connect to a Glee server. Conversely, as a server, it accepts connections from other Glee clients. Thus, a Glee application can accept connections from clients while also sending requests to other Glee servers. -When a security requirement is specified in the `asyncapi.yaml` file and Glee acts as a server, the `serverAuth` function should be implemented, if Glee acts as a client then the `clientAuth` function should be implemented. If Glee is being used as both client and server, then it should have both the `clientAuth` and `serverAuth` functions. +If a security requirement is specified in the `asyncapi.yaml` file, and Glee acts as a server, the `serverAuth` function should be implemented. If Glee acts as a client, then `clientAuth` should be implemented. If Glee is used as both client and server, both functions are necessary. ## Server Authentication in Glee -The `serverAuth` function takes an argument that can be destructured as follows +The `serverAuth` function takes an argument that can be destructured as follows: | Attribute | Description | | ---------- | --------------------------------------------------------------- | -| done | The done function that tells the server to proceed. | -| authProps | The authentication parameters recieved from the client. | -| serverName | The name of the server/broker from which the event was emitted. | -| doc | The parsedAsyncAPI schema | +| done | The function that signals the server to proceed. | +| authProps | The authentication parameters received from the client. | +| serverName | The name of the server/broker emitting the event. | +| doc | The parsed AsyncAPI schema. | -#### done() function +#### done() Function -The `done()` parameter in the `serverAuth` function allows the broker/server to know what to do next depending on the boolean value you pass to it. +The `done()` parameter in the `serverAuth` function signals to the broker/server what action to take next, based on the boolean value passed. ```js /* websocket.js */ @@ -113,22 +110,22 @@ export async function serverAuth({ authProps, done }) { } } ``` + **Parameters for done():** -*Authentication Result (Boolean): true for success, false for failure.* +- Authentication Result (Boolean): `true` for success, `false` for failure. -When `true` is passed to the done parameter, the server/broker knows to go ahead and allow the client to connect, which means authentication has succeeded. However if the `done` parameter is called with `false` then the server knows to throw an error message and reject the client, which means authentication has failed. +Passing `true` to the `done` parameter indicates that authentication has succeeded, and the server/broker can proceed to allow the client to connect. Conversely, if `false` is passed, the server will reject the client, indicating failed authentication. -`done()` should always be the last thing called in a `serverAuth` function, Glee won't execute any logic beyond the `done()` call. +The `done()` call should always be the last in the `serverAuth` function, as Glee will not execute any logic beyond this call. #### authProps -`authProps` implements a couple of methods that allows the server to retrieve the authentication parameters from the client, below are the current available methods; +The `authProps` parameter includes methods for the server to retrieve authentication parameters from the client. The current available methods are as follows: ```js export async function serverAuth({ authProps, done }) { - //some network request - + // Some network request authProps.getOauthToken() authProps.getHttpAPIKeys('api_key') authProps.getToken() @@ -140,23 +137,23 @@ export async function serverAuth({ authProps, done }) { | Method | Description | | ---------------------- | ------------------------------------------------------------------------------------------------ | -| `getOauthToken()` | returns the oauth authentication parameter | -| `getHttpAPIKeys(name)` | returns the HttpAPIKeys parameter with the specified name from either headers or query parameter | -| `getToken()` | returns the http bearer token parameter | -| `getUserPass()` | returns username and password parameters | +| `getOauthToken()` | Returns the OAuth authentication parameter. | +| `getHttpAPIKeys(name)` | Returns the HttpAPIKeys parameter with the specified name from either headers or query parameter | +| `getToken()` | Returns the HTTP bearer token parameter. | +| `getUserPass()` | Returns username and password parameters. | ## Client Authentication in Glee -The `clientAuth` function also takes an argument, and it's argument can be destructured as follows +The `clientAuth` function also takes an argument that can be destructured as follows: | Attribute | Description | | -------------- | ------------------------------------------------------------------------------------- | -| parsedAsyncAPI | The parsedAsyncAPI schema. | -| serverName | The name of the server/broker from with the authentication parameters are being sent. | +| parsedAsyncAPI | The parsed AsyncAPI schema. | +| serverName | The server/broker's name from which the authentication parameters are being sent. | -### Possible authentication parameters +### Possible Authentication Parameters -The possible authentication parameters are shown in the code snippet below: +The code snippet below illustrates the possible authentication parameters: ```js export async function clientAuth({ serverName }) { @@ -165,18 +162,18 @@ export async function clientAuth({ serverName }) { oauth: process.env.OAUTH2, apiKey: process.env.APIKEY, userPass: { - user: process.env.user, - password: process.env.password, + user: process.env.USER, + password: process.env.PASSWORD, }, } } ``` -The name of the authentication parameters should be the same as **the names specified in the `asyncapi.yaml` file.** +The names of the authentication parameters should match **the names specified in the `asyncapi.yaml` file**. -| auth type | values | +| Auth Type | Values | | ------------------------------------- | ---------------------------------------------------------------------- | -| http bearer (JWT) | Value should be a JWT string | -| Oauth2 | The value should should be a string | -| httpApiKey in headers or query params | The value should be a string | -| userPass | The value should be an object with the user and password as properties | +| HTTP bearer (JWT) | Value should be a JWT string. | +| OAuth2 | Value should be a string. | +| httpApiKey in headers or query params | Value should be a string. | +| userPass | Value should be an object with the user and password as properties. | \ No newline at end of file diff --git a/markdown/docs/tools/glee/bearerToken.md b/markdown/docs/tools/glee/bearerToken.md index db22c2b6c9ad..9bc60e394fc8 100644 --- a/markdown/docs/tools/glee/bearerToken.md +++ b/markdown/docs/tools/glee/bearerToken.md @@ -1,21 +1,21 @@ --- -title: 'Http Authentication(Bearer Token)' +title: 'HTTP Authentication (Bearer Token)' weight: 80 --- -## Getting started with Bearer Token authentication +## Getting Started with Bearer Token Authentication -Bearer Token authentication is one of the most popular forms of authentication and is widely used because of its perceived security. This guide will walk through how to implement bearer token authentication in Glee. +Bearer Token authentication is one of the most popular forms of authentication and is widely used due to its perceived security. This guide will walk you through how to implement bearer token authentication in Glee. -A sample `asyncapi.yaml` for a server with security requirements and user password security scheme is shown below: +Below is a sample `asyncapi.yaml` for a server with security requirements and a user password security scheme: ```yaml -##server asyncAPI schema +## Server AsyncAPI Schema asyncapi: 3.0.0 info: - title: AsyncAPI IMDB server + title: AsyncAPI IMDB Server version: 1.0.0 - description: This app is a dummy server that would stream the trending/upcoming anime. + description: This app is a dummy server that streams trending/upcoming anime. servers: trendingAnimeServer: host: 'localhost:8081' @@ -37,7 +37,7 @@ components: A sample `asyncapi.yaml` for a client that implements some of the requirements of the server above: ```yaml -##client asyncAPI schema +## Client AsyncAPI Schema servers: trendingAnime: host: localhost:8081 @@ -61,49 +61,46 @@ components: ``` -The Client asyncapi.yaml file **does't need to implement all the security requirements in the server, it only needs to implement the ones that it uses like *http (bearer token)* here.** +The Client `asyncapi.yaml` file **doesn't need to implement all the security requirements of the server; it only needs to implement the ones it uses, like *http (bearer token)* here.** ### Client Side -Following the client `asyncapi.yaml` file above, create a file named `trendingAnime.ts` in the `auth` directory, since that is the server that has the security Property. +Following the client `asyncapi.yaml` file above, create a file named `trendingAnime.ts` in the `auth` directory, since that is the server that has the security property. ```bash touch auth/trendingAnime.ts ``` -When using the `bearer` security scheme, it is important that you pass the parameters as follows: +When using the `bearer` security scheme, pass the parameters as follows: ```js -export async clientAuth({ parsedAsyncAPI, serverName }) { +export async function clientAuth({ parsedAsyncAPI, serverName }) { return { token: process.env.TOKEN } } ``` -Glee will utilize the `token` for server authentication, employing it in the header with the format: Authorization: Bearer {token}. +Glee will utilize the `token` for server authentication, employing it in the header with the format: `Authorization: Bearer \{token\}`. -### Server side +### Server Side -From the server `asyncapi.yaml` file above, create a file named `trendingAnimeServer.ts` in the `auth` directory, since that is the server that has the security Property. +From the server `asyncapi.yaml` file above, create a file named `trendingAnimeServer.ts` in the `auth` directory, since that is the server that has the security property. ```bash touch auth/trendingAnimeServer.ts ``` -On the server side, you can retrieve the values as follows +On the server side, you can retrieve the values as follows: ```js -export async serverAuth({ authProps, done }) { +export async function serverAuth({ authProps, done }) { authProps.getToken() - // your authentication logic here... - done(true|false) + // Your authentication logic here... + done(true || false) } ``` -So, `getToken()` returns a string which contains the token that is sent from the client. - - - +So, `getToken()` returns a string containing the token sent from the client. \ No newline at end of file diff --git a/markdown/docs/tools/glee/crypto-websockets-interactive.md b/markdown/docs/tools/glee/crypto-websockets-interactive.md index 8e0934950725..70a87b7f2ca5 100644 --- a/markdown/docs/tools/glee/crypto-websockets-interactive.md +++ b/markdown/docs/tools/glee/crypto-websockets-interactive.md @@ -14,4 +14,4 @@ Please become our alpha testers of the tutorial: 2. Let us know what you think using the channel that works for you the best: - [Slack](https://www.asyncapi.com/slack-invite/) - [Twitter](https://twitter.com/AsyncAPISpec) - - [GitHub Issue](https://github.com/asyncapi/glee/issues/) + - [GitHub Issue](https://github.com/asyncapi/glee/issues/) \ No newline at end of file diff --git a/markdown/docs/tools/glee/env-vars-config.md b/markdown/docs/tools/glee/env-vars-config.md index e48418a44a9d..9c8590e28c9f 100644 --- a/markdown/docs/tools/glee/env-vars-config.md +++ b/markdown/docs/tools/glee/env-vars-config.md @@ -1,195 +1,136 @@ --- -title: Environment variables and Configuration file +title: Configuring Environment Variables and Configuration File weight: 50 --- -# Environment Variables +## Environment Variables -Glee provides a few environment variables for you to customize the Glee application's behavior according to your specific requirements: +Glee provides several environment variables that allow you to tailor your application's behavior to fit specific needs: -|Variable|Description|Example| -|---|---|---| -|GLEE_SERVER_NAMES|A comma-separated list of the servers to load on startup.|`GLEE_SERVER_NAMES=websockets,mosquitto`| -|GLEE_SERVER_CERTS|A comma-separated list of `${serverName}:${pathToCertificateFile}`. These are the certificates to use when establishing the connection to the given server.|`GLEE_SERVER_CERTS=mosquitto:mosquitto.org.crt`| -|GLEE_SERVER_VARIABLES|A comma-separated list of `${serverName}:${serverVariable}:${value}`. These are the values to use for each server variable.|`GLEE_SERVER_VARIABLES=websockets:namespace:public`| +|Variable|Purpose|Example Usage| +|--------|-------|-------------| +|GLEE_SERVER_NAMES|Specifies a list of servers to initialize at startup, separated by commas.|`GLEE_SERVER_NAMES=websockets,mosquitto`| +|GLEE_SERVER_CERTS|Indicates server-specific certificate files in a `${serverName}:${pathToCertificateFile}` format, separated by commas.|`GLEE_SERVER_CERTS=mosquitto:mosquitto.org.crt`| +|GLEE_SERVER_VARIABLES|Sets server variables in a `${serverName}:${serverVariable}:${value}` format, separated by commas.|`GLEE_SERVER_VARIABLES=websockets:namespace:public`| -## Configuring Glee +### Handling Multiple .env Files +Glee supports loading variables from `.env.local` directly into `process.env`. This feature is handy for keeping secrets out of your repository during development. You can also set environment-specific defaults in `.env.development` or `.env.production`. -Glee comes with sensible defaults so you don't have to worry about configuration. However, sometimes you may want to change the behavior or customize Glee in different ways. For that purpose, you can use the `glee.config.js` file. +`.env.local` takes precedence over other `.env*` files. -### Configuration file +Switch between `development` and `production` environments by setting the `NODE_ENV` variable accordingly. -Glee's config file is a JavaScript file that exports an async function. Something like this: +## Customizing Glee Settings + +While Glee comes with defaults for ease of use, you may want to customize settings for specific needs. This is where `glee.config.js` comes into play. + +### The Glee Config File + +`glee.config.js` is a JavaScript file exporting an asynchronous function, structured as follows: ```js export default async function () { - // More stuff here... + // Configuration details go here... } ``` -This function must return an object with the following shape: +This function should return an object with configurable properties: ```js export default async function () { return { glee: {}, - kafka: {}, websocket: {}, - mqtt: {}, cluster: {}, http: {} } } - ``` -Here is an example of a `glee.config.js` file for reference: +For example, a typical `glee.config.js` might look like this: ```js export default async function () { return { - glee: { // Glee core configurations + glee: { // Core Glee configurations lifecycleDir: './lifecycle', functionsDir: './functions', asyncapiFilePath: './asyncapi.json', - logs: { // you can change the defualt behaviour of glee which logs everything by default. - incoming: 'channel-only', // only logs the channel not message payload. - outgoing: 'none', //log nothing. + logs: { // Adjust default logging behavior + incoming: 'channel-only', // Logs only the channel, not the message payload + outgoing: 'none', // Disables outgoing logs } }, docs: { - enabled: true, // Enable/Disable documentation generation - folder: 'docs', // Folder where you want the output of your docs to reside. - template: '@asyncapi/markdown-template' // Type of template you want to use. - } + enabled: true, // Toggles documentation generation + folder: 'docs', // Destination folder for docs + template: '@asyncapi/markdown-template' // Specifies the documentation template + }, ws: { server: { - httpServer: customServer, // A custom HTTP server of your own. - adapter: "native", // Default. Can also be 'socket.io' or a reference to a custom adapter. - port: process.env.PORT, + httpServer: customServer, // Custom HTTP server + adapter: "native", // Defaults to 'native', can be 'socket.io' or a custom adapter + port: process.env.PORT, // Server port } }, cluster: { - adapter: "redis", - name: "cluster", // Default. Name of your cluster. - url: "redis://localhost:6379", // Server URL used by adapter for clustering - }, - mqtt: { - auth: ({serverName, parsedAsyncAPI}) => { - if (serverName === 'mqtt') { - return { - cert: async () => fs.readFileSync('./cert') - clientId: '123', - username: 'user1' - password: 'pass12' - } - } - } + adapter: "redis", // Cluster adapter, default is Redis + name: "cluster", // Cluster name + url: "redis://localhost:6379", // URL for the cluster server (Redis in this case) }, http: { server: { - httpServer: customServer, // A custom HTTP server of your own. + httpServer: customServer, // Custom HTTP server adapter: 'native', - port: process.env.PORT, + port: process.env.PORT, // Server port }, - client: { - auth: { - token: process.env.TOKEN - }, - query: { - foo: 'bar' - }, - body: { - foo: 'bar' - } - } } }; } ``` -Inside the return statement, you can specify the following options: -#### Glee Core Configurations -These configurations apply to Glee itself, rather than any specific protocol. +In the return statement, configure the following options: -|Field|Default|Description| +#### Core Glee Configurations +These settings are specific to Glee itself. + +|Field|Default|Purpose| |--|--|--| -|glee.gleeDir|`.glee`|Sets the Glee directory. Your sources will be compiled here.| -|glee.lifecycleDir|`lifecycle`|Path to the directory that stores your [lifecycle events](./lifecycle-events.md).| -|glee.functionsDir|`functions`| Path to the directory that stores your [functions](./functions.md).| -|glee.asyncapiFilePath|`asyncapi.(yaml \| yml \| json)`| Path to your AsyncAPI file.| -|glee.logs| default | glee logs channel and payload by default. you can change this behaviour for incoming and outgoing messages.| -|glee.logs.incoming| "all" | Supported values are `channel-only` and `none`.| -|glee.logs.outgoing| "all" | Supported values are `channel-only` and `none`.| - -#### Generating Documentation -|Field|Description| -|--|--| -|docs.enabled|This flag enables/disables the docs generation functionality. -|docs.folder|The dedicated folder you want your generated docs to reside. -|docs.template|The AsyncAPI template you wanna use for generating your documentation. -#### Websocket Server -|Field|Description| +|glee.gleeDir|`.glee`|Determines the Glee directory for compiled sources.| +|glee.lifecycleDir|`lifecycle`|Specifies the path to [lifecycle events](./lifecycle-events.md).| +|glee.functionsDir|`functions`|Designates the path to [functions](./functions.md).| +|glee.asyncapiFilePath|`asyncapi.(yaml \| yml \| json)`|Path to your AsyncAPI file.| +|glee.logs|default|Configures logging for incoming and outgoing messages.| +|glee.logs.incoming|"all"|Options: `channel-only`, `none`.| +|glee.logs.outgoing|"all"|Options: `channel-only`, `none`.| + +#### Documentation Configuration +|Field|Purpose| |--|--| -|ws.server|Websocket server-specific configurations| -|ws.server.adapter| The Glee adapter to use for the WebSocket server. Defaults to a "native" WebSocket implementation. Other allowed values are `socket.io` (to use the [Socket.IO](https://socket.io/) Glee adapter) or a reference to a custom adapter.| -|ws.server.httpServer| A custom HTTP server of your own. E.g., an [Express](https://expressjs.com/en/4x/api.html) server or any object that implements the [http.Server](https://nodejs.org/api/http.html#http_class_http_server) interface. | -|ws.server.port| The port to use when binding the WebSocket server. This is useful when your server is behind a proxy and the port exposed for consumption is not the same as the port your application should be bound to. Defaults to the port specified in the selected AsyncAPI server.| -#### Cluster -|Field|Description| +|docs.enabled|Enables or disables documentation generation.| +|docs.folder|Specifies the output directory for documentation.| +|docs.template|Determines the AsyncAPI template for docs generation.| + +#### WebSocket Server Configuration +|Field|Purpose| |--|--| -|cluster.adapter| The Glee cluster adapter to use for communication between instances. Defaults to Redis Pub/Sub ("redis"). Can be a reference to a custom adapter.| -|cluster.name|The name of the cluster. Defaults to "cluster".| -|cluster.url|The url of the server to be used by the adapter. In case of "redis" adapter, it's the url of the Redis server.| -#### MQTT -|Field|Description| -|---|---| -|mqtt.auth| MQTT authentication configuration| -|mqtt.auth.cert| Client certificate -|mqtt.auth.clientId| MQTT client Id for authentication -|mqtt.auth.username| username parameter -|mqtt.auth.password| password parameter -#### Kafka -|Field|Description| -|---|---| -|kafka.auth| Kafka authentication configuration| -|kafka.auth.key | Kafka Broker Key -|kafka.auth.cert| Client certificate -|kafka.auth.clientId| Kafka client Id for authentication -|kafka.auth.rejectUnauthorized | Boolean flag for accepting the valid SSL certificates -|kafka.auth.username| The username to use during authentication. -|kafka.auth.password| The password to use during authentication. -#### HTTP Server -|Field|Description| +|ws.server|WebSocket server-specific settings.| +|ws.server.adapter|Selects the WebSocket server adapter: `native`, `socket.io`, or a custom one.| +|ws.server.httpServer|A custom HTTP server instance.| +|ws.server.port + +|The port for the WebSocket server.| + +#### Cluster Configuration +|Field|Purpose| |--|--| -|http.server|HTTP server-specific configurations| -|http.client|HTTP client-specific configurations| -|http.server.adapter| The Glee adapter to use for the HTTP server. Defaults to a "native" HTTP implementation.| -|websocket.server.port| The port to use when binding the HTTP server. This is useful when your server is behind a proxy and the port exposed for consumption is not the same as the port your application should be bound to. Defaults to the port specified in the selected AsyncAPI server.| -|http.client.auth| Authentication/Authorization configuration for the client| -|http.client.auth.token| HTTP Authentication header| -|http.client.query| Query object for the client to send| -|http.client.body| Body object for the client to send -#### Auth Config -Most clients like `ws`,`kafka`, and `mqtt` have auth fields that are used for passing auth parameters. All these configurations can be an object or a function that returns the specific object defined by each protocol. +|cluster.adapter|Chooses the cluster communication adapter (default: Redis Pub/Sub).| +|cluster.name|The cluster's name.| +|cluster.url|URL of the server used by the cluster adapter.| -```js -export default async function() { - ws: { - client: { - auth: { - token: process.env.TOKEN - } - } - }, - mqtt: { - auth: ({serverName, parsedAsyncAPI}) => { - if (serverName === 'mqtt') { - return { - cert: fs.readFileSync('./cert', 'utf-8') - } - } - } - } -} -``` +#### HTTP Server Configuration +|Field|Purpose| +|--|--| +|http.server|HTTP server-specific settings.| +|http.server.adapter|Selects the HTTP server adapter.| +|http.server.port|The port for the HTTP server.| diff --git a/markdown/docs/tools/glee/function-lifecycle-events.md b/markdown/docs/tools/glee/function-lifecycle-events.md index d47f8f87bcf1..3d10988d08ce 100644 --- a/markdown/docs/tools/glee/function-lifecycle-events.md +++ b/markdown/docs/tools/glee/function-lifecycle-events.md @@ -1,5 +1,5 @@ --- -title: Function and Lifecycle events +title: Functions and Lifecycle Events weight: 40 --- @@ -13,40 +13,43 @@ export default async function (event) { } ``` -Functions take a single argument, which is the event received from a broker or a client, depending which kind of API you're building. The `event` argument has the following shape: +Functions take a single argument, which is the event received from a broker or a client, depending on the type of API you're building. The `event` argument has the following structure: |Attribute|Description| -|----|----| -|payload|The payload/body of the received event. -|headers|The headers/metadata of the received event. -|channel|The name of the channel/topic from which the event was read. -|serverName|The name of the server/broker from which the event was received. +|---------|-----------| +|payload|The payload/body of the received event.| +|headers|The headers/metadata of the received event.| +|channel|The name of the channel/topic from which the event was read.| +|serverName|The name of the server/broker from which the event was received.| -Functions may return an object to tell Glee what to do next. For instance, the following example greets the user back: +Functions may return an object to instruct Glee on what action to take next. For instance, the following example sends a greeting message to the `development` server: ```js /* onHello.js */ export default async function (event) { return { - reply: [{ + send: [{ + server: 'development', + channel: 'greets', payload: 'Greetings! How is your day going?' }] - } + }; } ``` |Attribute|Type|Description| -|---|---|---| -|send|array<[OutboundMessage](#anatomy-of-an-outbound-message)>|A list of outbound messages to send when the processing of the inbound event has finished. All clients subscribed to the given channel/topic will receive the message. -|reply|array<[OutboundMessage](#anatomy-of-an-outbound-message)>|A list of outbound messages to send as a reply when the processing of the inbound event has finished. This is useful when the target of your message is the sender of the inbound event. Note, however, that this only works when you're running Glee as a server. For example, using `reply` when receiving a WebSocket message is fine and the reply will exclusively go to the client that sent the message. However, if you're receiving a message from an MQTT broker, `reply` will work exactly the same way as `send` above, and will send the message to all the clients subscribed to the given channel/topic. -##### Anatomy of an outbound message +|---------|----|-----------| +|send|array<[OutboundMessage](#anatomy-of-an-outbound-message)>|A list of outbound messages to send after processing the inbound event. All clients subscribed to the given channel/topic will receive the message. + +##### Anatomy of an Outbound Message |Attribute|Type|Description| -|---|---|---| -|payload|string|The payload/body of the message you want to send. -|headers|object<string,string>|The headers/metadata of the message you want to send. -|channel|string|The channel/topic you want to send the message to. Defaults to `event.channel`, i.e., the same channel as the received event. -|server|string|The server/broker you want to send the message to. Defaults to `event.serverName`, i.e., the same server as the received event. -## How does Glee know which function it should execute? -Glee reads your `asyncapi.yaml` file and searches for all the `receive` actions containing an `operations` attribute field. The `operations` field serves as a mechanism to bind a given operation to a specific function file. For instance, given the folowing AsyncAPI definition: +|---------|----|-----------| +|payload|string|The payload/body of the message you want to send.| +|headers|object<string,string>|The headers/metadata of the message you want to send.| +|channel|string|The channel/topic to which you want to send the message. Defaults to `event.channel`, i.e., the same channel as the received event.| +|server|string|The server/broker to which you want to send the message. Defaults to `event.serverName`, i.e., the same server as the received event.| + +## How Does Glee Determine Which Function to Execute? +Glee reads your `asyncapi.yaml` file and looks for all the `receive` actions containing an `operations` attribute field. The `operations` field serves as a mechanism to bind a specific operation to a function file. For instance, given the following AsyncAPI definition: ```yaml ... operations: @@ -61,9 +64,9 @@ Glee maps the `onHello` operation to the `functions/onHello.js` file. # Lifecycle Events -Glee lets you bind incoming messages to functions. However, sometimes we need to be proactive and be the first ones to send a message, not necessarily as a reaction to another message. Use cases can be very diverse: from sending a message to announce our client is connected to sending a message every few seconds or minutes. +Glee allows you to bind incoming messages to functions. However, sometimes it's necessary to initiate communication proactively, not merely as a reaction to another message. Use cases can vary widely: from sending a message to announce that our client is connected, to broadcasting messages at regular intervals. -To subscribe to a lifecycle event, create a file under the `lifecycle` directory. It must have the following shape: +To subscribe to a lifecycle event, create a file under the `lifecycle` directory. It must be structured as follows: ```js export default async function ({ glee, @@ -77,41 +80,40 @@ export default async function ({ export const lifecycleEvent = 'onConnect' ``` -Each file in the `lifecycle` directory must export a default async function and the `lifecycleEvent` field, which is the [name of the event](#list-of-events) you want to subscribe to. Optionally, your function can return an object following exactly the same syntax as described above in the functions definition. +Each file in the `lifecycle` directory must export a default async function and the `lifecycleEvent` field, indicating the [name of the event](#list-of-events) you wish to subscribe to. Optionally, your function can return an object following the same syntax as described above in the functions definition. -## List of events +## List of Events |Event|Description| -|---|---| -|onConnect|A connection with a broker has been established. -|onReconnect|Glee reconnected to a broker. -|onDisconnect|A connection with a broker has been closed. -|onServerReady|Your Glee server is now ready to accept connections. -|onServerConnectionOpen|A client has opened a connection with your Glee server. -|onServerConnectionClose|A client has closed the connection with your Glee server. - -All of them take a single argument which contains information about the event: - -|Attribute|Description -|---|---| -|glee|A reference to the Glee app. -|serverName|The name of the server where the event happened. -|server|The AsyncAPI definition of the server where the event happened. -|connection|The connection where the event happened. - -## Restricting the lifecycle event - -In some cases it's useful to restrict the lifecycle event to a specific server or set of servers. To do that, add a line like the following to your lifecycle file: +|-----|-----------| +|onConnect|A connection with a broker has been established.| +|onReconnect|Glee reconnected to a broker.| +|onDisconnect|A connection with a broker has been closed.| +|onServerReady|Your Glee server is now ready to accept connections.| +|onServerConnectionOpen|A client has opened a connection with your Glee server.| +|onServerConnectionClose|A client has closed the connection with your Glee server.| + +All of them take a single argument containing information about the event: + +|Attribute|Description| +|---------|-----------| +|glee|A reference to the Glee app.| +|serverName|The name of the server where the event occurred.| +|server|The AsyncAPI definition of the server where the event occurred.| +|connection|The connection where the event occurred.| + +## Restricting the Lifecycle Event + +In some cases, it's useful to restrict the lifecycle event to a specific server or set of servers. To do this, add a line like the following to your lifecycle file: ```js export const servers = ['mosquitto'] ``` -The above example makes Glee fire the lifecycle event only if it's coming from the `mosquitto` server. +The above example ensures Glee fires the lifecycle event only if it originates from the `mosquitto` server. -Additionally, you may want to restrict the lifecycle event by channel/topic. To do that, add a line like the following to your lifecycle file: +Similarly, you may want to restrict the lifecycle event to a specific channel/topic. Add a line like this to your lifecycle file: ```js export const channels = ['user/signedup'] ``` -The above example makes Glee fire the lifecycle event only if the connection has the channel `user/signedup` listed as one of its channels. -Glee maps the `onHello` operation to the `functions/onHello.js` file. +The above example ensures Glee fires the lifecycle event only if the connection includes the channel `user/signedup`. diff --git a/markdown/docs/tools/glee/glee-auth-intro.md b/markdown/docs/tools/glee/glee-auth-intro.md index 73a136df9c14..8689ce36dbf8 100644 --- a/markdown/docs/tools/glee/glee-auth-intro.md +++ b/markdown/docs/tools/glee/glee-auth-intro.md @@ -3,20 +3,19 @@ title: 'Introduction to Glee Authentication' weight: 60 --- -Glee comes with Authentication features which help you verifying the identity of users or entities attempting to access a system or application. It ensures that only authorised individuals or systems are granted access, protecting against unauthorised intrusions and data breaches. Glee simplifies this vital process by offering multiple authentication methods, each tailored to different use cases. Following methods are the different ways to sheild your application access. +Glee comes with authentication features that help you in verifying the identity of users or entities attempting to access a system or application. It ensures that only authorized individuals or systems are granted access, protecting against unauthorized intrusions and data breaches. Glee simplifies this vital process by offering multiple authentication methods, each tailored to different use cases. The following methods are different ways to shield your application access. ### Authentication Using Authentication Functions: -Glee allows you to implement custom authentication logic by utilising authentication functions. This flexible approach enables developers to craft tailored authentication mechanisms, ensuring that access to resources is controlled precisely as required. - +Glee allows you to implement custom authentication logic by utilizing authentication functions. This flexible approach enables developers to craft tailored authentication mechanisms, ensuring that access to resources is controlled precisely as required. ### HTTP Bearer Token Authentication: In today's API-driven world, bearer token authentication is a widely adopted method. Glee supports this approach, allowing clients to present a token as proof of their identity, thus ensuring secure and efficient access to resources. -### HttpApiKey Authentication: +### HTTP API Key Authentication: Glee's authentication suite includes support for API key authentication, which is vital for protecting web APIs. By using API keys, you can regulate access to your services, making it an essential component of your application's security strategy. ### Username and Password Authentication: Traditional yet still crucial, username and password authentication remains a reliable option within Glee's toolkit. This method allows users to access systems or applications by providing their unique credentials, ensuring a familiar and straightforward login experience. #### Summary -Glee's authentication features not only provide layers of security but also offer the flexibility needed to meet your unique requirements. Whether you're developing a web application, a mobile app, or any other application, Glee's authentication methods empower you to tailor your security measures to suit the demands of your project. With Glee, you can build and maintain a secure digital environment, ensuring that only authorised users and systems gain access, protecting your valuable data and resources. \ No newline at end of file +Glee's authentication features not only provide layers of security but also offer the flexibility needed to meet your unique requirements. Whether you're developing a web application, a mobile app, or any other type of application, Glee's authentication methods empower you to tailor your security measures to suit the demands of your project. With Glee, you can build and maintain a secure digital environment, ensuring that only authorized users and systems gain access, protecting your valuable data and resources. \ No newline at end of file diff --git a/markdown/docs/tools/glee/glee-template.md b/markdown/docs/tools/glee/glee-template.md deleted file mode 100644 index ec0db7a5087a..000000000000 --- a/markdown/docs/tools/glee/glee-template.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: "Create AsyncAPI Glee template" -weight: 30 ---- -This tutorial teaches you how to create a simple glee template. You'll use the AsyncAPI Glee template that you develop to generate Javascript code. Additionally, you'll create a template code with a reusable component to reuse the custom functionality you create and test your code using an WS server. - - -{`asyncapi: 3.0.0 -info: - title: 'Hello, Glee!' - version: 1.0.0 -servers: - websockets: - host: 0.0.0.0:3000 - protocol: ws -channels: - hello: - address: hello - messages: - hello: - $ref: '#/components/messages/hello' -operations: - onHello: - action: receive - channel: - $ref: '#/channels/hello' - reply: - channel: - $ref: "#/channels/hello" - SendHello: - action: send - channel: - $ref: "#/channels/hello" -components: - messages: - hello: - payload: - type: string`} - - -Let's break it down into pieces: - - -{`info: - title: 'Hello, Glee!' - version: 1.0.0`} - - -The `info` section provides general information about the API, including its title and version. - -Moving on, let's talk about the `servers` section. - - -{`servers: - websockets: - host: 0.0.0.0:3000 - protocol: ws`} - - -The servers section defines the different servers where the API can be accessed. In this case, there is a single server named "websockets" that uses the WebSocket protocol (`ws`) and listens on the address `ws://0.0.0.0:3000`. - -Now lets move on to the `channels` section. This section is used to describe the event names your API will be publishing and/or subscribing to. - - -{`channels: - hello: - address: hello - messages: - hello: - $ref: '#/components/messages/hello' -operations: - onHello: - action: receive - channel: - $ref: '#/channels/hello' - reply: - channel: - $ref: "#/channels/hello" - sendHello: - action: send - channel: - $ref: '#/channels/hello'`} - - -The channels section defines the communication channels available in the API. In this case, there's a channel named "hello". This channel supports both sending and receiving. - -The `receive` action indicates that messages received on the `hello` channel should follow the structure defined in the hello message component. Under this action, `reply` which is in a request-reply operation, contains the payload on `onHello.js` function. -The `send` action specifies that the operation with ID `sendHello` is used for sending messages to the `hello` channel. The message structure is referenced from the hello message component. - -Next is the `payload` property under `hello` message component which is used to understand how the event should look like when publishing to that channel: - - -{`components: - messages: - hello: - payload: - type: string`} - - -The components section contains reusable elements, in this case, a definition for the "hello" message. It specifies that the payload of the "hello" message should be of type string. - -## Summary - -In this tutorial, you learned how to create an AsyncAPI specification document via a simple example with a glee template. diff --git a/markdown/docs/tools/glee/httpApiKey.md b/markdown/docs/tools/glee/httpApiKey.md index ffa3dd0e8267..587851f2f4f6 100644 --- a/markdown/docs/tools/glee/httpApiKey.md +++ b/markdown/docs/tools/glee/httpApiKey.md @@ -3,19 +3,19 @@ title: 'HttpApiKey Authentication' weight: 90 --- -## Getting started with httpAPIKey authentication +## Getting Started with HttpAPIKey Authentication -This guide will walk through how to implement authentication using the `httpAPiKey` security scheme in Glee. +This guide will walk you through how to implement authentication using the `httpApiKey` security scheme in Glee. -A sample `asyncapi.yaml` for a server with security requirements and user `HttpApiKey` security scheme is shown below: +Below is a sample `asyncapi.yaml` for a server with security requirements and the `HttpApiKey` security scheme: ```yaml -##server asyncAPI schema +## Server AsyncAPI Schema asyncapi: 3.0.0 info: - title: AsyncAPI IMDB server + title: AsyncAPI IMDB Server version: 1.0.0 - description: This app is a dummy server that would stream the trending/upcoming anime. + description: This app is a dummy server that streams the trending/upcoming anime. servers: trendingAnimeServer: host: 'localhost:8081' @@ -37,7 +37,7 @@ components: A sample `asyncapi.yaml` for a client that implements some of the requirements of the server above: ```yaml -##client asyncAPI schema +## Client AsyncAPI Schema servers: trendingAnime: host: localhost:8081 @@ -58,38 +58,36 @@ components: type: httpApiKey name: api_key in: query - ``` -The `httpApiKey` could be in either the header or query parameter. +The `httpApiKey` can be located in either the header or query parameter. -The Client asyncapi.yaml file **does not need to implement all the security requirements in the server, it only needs to implement the ones that it uses like *httpApiKey* here.** +The client `asyncapi.yaml` file **does not need to implement all the security requirements of the server; it only needs to implement the ones it uses, like *httpApiKey* here.** ### Client Side -Following the client `asyncapi.yaml` file above, create a file named `trendingAnime.ts` in the `auth` directory, since that is the server that has the security Property. +Following the client `asyncapi.yaml` file above, create a file named `trendingAnime.ts` in the `auth` directory, as this is the server that has the security property. ```bash touch auth/trendingAnime.ts ``` -When using the `HttpApiKey` security scheme, it is important that you pass the parameters as follows: +When using the `HttpApiKey` security scheme, it is important to pass the parameters as follows: ```js -export async clientAuth({ parsedAsyncAPI, serverName }) { +export async function clientAuth({ parsedAsyncAPI, serverName }) { return { apiKey: process.env.APIKEY } } ``` -`apiKey` should be the name of the security requirement as specified in your `asyncapi.yaml` file, and it's value should be a string. +`apiKey` should be the name of the security requirement as specified in your `asyncapi.yaml` file, and its value should be a string. +### Server Side -### Server side - -From the server `asyncapi.yaml` file above, create a file named `trendingAnimeServer.ts` in the `auth` directory, since that is the server that has the security Property. +From the server `asyncapi.yaml` file above, create a file named `trendingAnimeServer.ts` in the `auth` directory, as this is the server that has the security property. ```bash touch auth/trendingAnimeServer.ts @@ -99,8 +97,8 @@ On the server side, you can retrieve the values as follows: ```js -export async serverAuth({ authProps, done }) { - authProps.getHttpAPIKeys('api_key')() +export async function serverAuth({ authProps, done }) { + authProps.getHttpAPIKeys('api_key') done(true) } diff --git a/markdown/docs/tools/glee/index.md b/markdown/docs/tools/glee/index.md index ef5638eec358..0f401af13030 100644 --- a/markdown/docs/tools/glee/index.md +++ b/markdown/docs/tools/glee/index.md @@ -1,17 +1,17 @@ --- -title: Getting Started +title: Kickstarting Your Journey with Glee weight: 10 --- -## Introduction +## Welcome to Glee -[Glee](https://github.com/asyncapi/glee) is a spec-first framework that helps you build server-side applications. That means it operates on the principle of defining the API specification (AsyncAPI) before diving into the actual implementation of the application logic. It leverages that principle to make you more productive: +[Glee](https://github.com/asyncapi/glee) introduces a refreshing approach to building server-side applications, emphasizing a spec-first methodology. This means your journey begins with defining the API specification (AsyncAPI) before writing a single line of application logic. Here's how Glee enhances your development experience: -- Glee ensures your code and AsyncAPI definition are on par, eliminating the problem of outdated documentation. By having both the code and the AsyncAPI definition in sync, you can ensure that the API documentation is always up to date, accurate, and reflects the current state of the application. Glee takes care of this automatically for you. -- Glee lets you focus on what matters and handles the rest for you. You only write the code for your business use-case. Glee takes care of performance, scalability, resilience, and everything you need to make your application production-ready. -- Glee validates the schema of the payload that it receives, if it doesn't conform to the schema that is defined in the AsyncAPI document, it throw an error telling user that the server received an invalid payload. +- **Always Updated Documentation**: Glee aligns your codebase with the AsyncAPI definition, ensuring your API documentation is constantly updated and reflective of your application's current capabilities. +- **Developer Centricity**: With Glee, your focus remains on the business logic. Leave the concerns of performance, scalability, and resilience to Glee, as it equips your application to be production-ready from the get-go. +- **Schema Validation**: Glee rigorously checks incoming payloads against the schema outlined in your AsyncAPI document. Any discrepancies result in an error, maintaining the integrity and reliability of your server's data processing. -Now, before you get started with a glee project, let's take a high level view of Application structure what glee resonates with. +Before you dive into Glee, let's explore its application structure and understand what makes Glee unique. ## Application structure @@ -35,121 +35,8 @@ Glee expects your project to have some files and folders with special names. Whe |functions|**Required.** This directory contains all the functions that Glee must execute when it receives a message from the server. Each file must export a default async function. |lifecycle|This directory contains application lifecycle functions. These functions will be executed when certain events happen in the application. E.g., `onConnect`, `onServerReady`, `onDisconnect`, etc. |.env|The environment variables of your application. **DO NOT PUT SECRETS HERE**. -|asyncapi.(yaml or json or yml)|**Required.** The [AsyncAPI](https://www.asyncapi.com/docs/specifications/latest) file defines your API. Make sure all the `publish` operations have an assigned `operationId` that matches a file name (excluding the extension) in the `functions` directory. -|glee.config.js| The Glee configuration file. +|asyncapi.(yaml or json or yml)|**Required.** The [AsyncAPI](https://www.asyncapi.com/docs/specifications/latest) file defines your API. Make sure all the `receive` operations have a name that matches a file name (excluding the extension) in the `functions` directory. +|glee.config.js| The Glee [configuration file](./env-vars-config.md). |package.json|**Required.** The Node.js package definition file. Make sure you include `@asyncapi/glee` as a dependency and add two scripts: `dev` and `start`. They should be running `glee dev` and `glee start` respectively. To understand the structure in a broader way, please refer to the associated page's links. - -### Let's create a glee project to simplify the app structure - -We will consider a simple WebSocket API using glee to understand its magic. We will create a simple WebSocket server that receives a current time from the client and then send a "good morning", "good evening" or "good night" respectively. - -To setup a project, you should follow our installation page on how to setup glee on your environment. - -We recommend creating a new Glee app using our official CLI which sets up everything automatically. (You don't need to create an empty directory. create-glee-app will make one for you.) To create a project, run: `asyncapi new glee` - -Once the process is completed, you should have a new Glee app ready for development and find the files that were made. - -#### Define our Spec for our API - -Glee being a spec-first framework, development starts with defining your API spec. To know more details into it, you can follow glee template to understand it step by step. For our case we will define our API: - -```yaml -asyncapi: 3.0.0 -info: - title: Greet Bot - version: 1.0.0 -servers: - websockets: - host: 0.0.0.0:3000 - protocol: ws -channels: - greet: - address: greet - messages: - greet: - $ref: '#/components/messages/greet' - time: - $ref: '#/components/messages/time' - time: - address: time - messages: - time: - $ref: '#/components/messages/time' -operations: - onGreet: - action: receive - channel: - $ref: '#/channels/greet' - reply: - channel: - $ref: '#/channels/greet' - sendGreet: - action: send - channel: - $ref: '#/channels/time' -components: - messages: - time: - payload: - type: object - properties: - currentTime: - type: number - name: - type: string - greet: - payload: - type: string - -``` - -This will be the Specification that defines our API, in our case, it is very simple, as we will be sending a name and the time of the day, and our API will greet us accordingly. - -One thing to note here is the `operations` item, this is needed and is a crucial part of glee, as this is how we will be connecting our business logic with our spec, `onGreet` is the name of the function that will be called every time a certain operation occurs. In our case whenever `/greet` channel receives a message, `onGreet` function is called. - -#### Define our operation function - -Now for our case, we will be adding a file `functions/onGreet.js` and writing up the logic for parsing our time and sending a response. - -```javascript -export default async function (event) { - const { name, time } = event.payload - const t = new Date(time) - const curHr = t.getHours() - let response = '' - if (curHr < 12) { - response = `Good Morning ${name}` - } else if (curHr < 18) { - response = `Good Afternoon ${name}` - } else { - response = `Good Evening ${name}` - } - return { - reply: [ - { - payload: response, - }, - ], - } -} - -``` - -Every file in the functions folder acts as a handler to develop business logic for glee, every file should export an async function that receives an event parameter, where you have access to payload and server details. - -#### Running and testing our application - -We will execute the application and carry out testing with Postman to ensure that it is functioning as intended. - -Now to execute our glee application, just run: - -``` -npm run dev -# or -npm run start -``` -To send a WebSocket request with a payload e.g. `{"name":"john", "time": "1567906535"}` to `ws://localhost:3000/greet`, open Postman and checkout the endpoint: - -So, this is how easy it is to build a WebSocket API using Glee. You can also check out the example code [here](https://github.com/Souvikns/greet-bot). diff --git a/markdown/docs/tools/glee/installation.md b/markdown/docs/tools/glee/installation.md index b3d587e39f31..a4cfc62ad71c 100644 --- a/markdown/docs/tools/glee/installation.md +++ b/markdown/docs/tools/glee/installation.md @@ -1,42 +1,64 @@ --- -title: 'Installation guide' -weight: 20 + +title: 'Glee Installation Guide' +weight: 30 + --- -## Glee Installation +### Preparing for Installation + +Before starting with Glee, ensure that NPM, Node.js, and the [AsyncAPI CLI](https://github.com/asyncapi/cli) are already set up on your system. + +> Need help installing AsyncAPI CLI? Check out the [CLI Installation Guide](https://www.asyncapi.com/docs/tools/cli/installation). -Before installing Glee into your project, make sure you have pre-installed NPM, NodeJs and [AsyncAPI CLI](https://github.com/asyncapi/cli) tools on your system. +### Starting with Glee -### Automatic Installation +Creating a new Glee application can be approached in three distinct ways: + * [Begin with an existing AsyncAPI document](#initialize-with-an-asyncapi-document). + * [Use a predefined template](#start-with-a-template). + * [Opt for Manual Installation](#manual-installation) (recommended for advanced users keen on understanding Glee's inner workings). -The best way to get started with Glee is by using AsyncAPI CLI, which sets up everything automatically for you. -To create a project, run: +### Initialize with an AsyncAPI Document +If you already have an AsyncAPI specification file, you can jumpstart your project by using the `-f` or `--file` flag to specify the file path. The CLI will leverage this to set up your project. ```sh -asyncapi new glee +asyncapi new glee -f asyncapi.yaml ``` -> For more information on how to install the AsynAPI CLI, you can review the [CLI installation guide](https://www.asyncapi.com/docs/tools/cli/installation). +This command generates all necessary files. Just open the project in your editor, and start integrating your authentication and business logic. + +### Start with a Template + +For those without a custom AsyncAPI file and looking to experiment, starting with a template is an excellent choice. These are simple Glee projects that the AsyncAPI CLI can generate for you. + +Use the `-t` or `--template` flag to specify the template name. Currently, two templates are available: -On installation, you'll find next steps after your project created: +1) **default**: Responds with a string upon receiving a string. +2) **tutorial**: Designed for [this tutorial](https://www.asyncapi.com/docs/tutorials/generate-code). +To initiate a project with the default template: + +```sh +asyncapi new glee -t default ``` -Your project "project" has been created successfully! +After installation, follow these steps: + +``` Next steps: cd project - npm install + npm install --ignore-scripts npm run dev -Also, you can already open the project in your favorite editor and start tweaking it +Feel free to open the project in your preferred editor and start customizing it. ``` -While making twists to your application, you can follow along with our getting started guide on the relevant page. +Refer to our Getting Started guide for further assistance during customization. ### Manual Installation -To manually create a new app, create a new folder e.g. `myapp` so the folder structure would look like below; +For a hands-on setup, start by creating a new directory, e.g., `myapp`, and structure it as follows: ``` ├─ functions (required) @@ -51,14 +73,14 @@ To manually create a new app, create a new folder e.g. `myapp` so the folder str ├─ package.json (required) ``` -Install the required packages inside a new folder: +Inside this new folder, initialize and install Glee: ```js npm init -y npm install @asyncapi/glee ``` -Open your package.json file and add the following scripts: +Edit your `package.json` to include the following scripts: ```js { @@ -70,19 +92,18 @@ Open your package.json file and add the following scripts: } ``` -These scripts refer to the different stages of developing an application. - -- `glee docs`: This script generates documentation for your project using the "Glee" documentation tool. This documentation includes information about your project's APIs, modules, and usage instructions. - -- `glee dev`: This script is used for starting a development server. It launches a local development server, build your project in development mode, or perform other development-related tasks. +These scripts serve different development stages: -- `glee start`: This script is responsible for starting your project or application. It is used to launch a production-ready server or application instance. +- `glee docs`: Generates your project documentation. +- `glee dev`: Starts a local development server, building your project in a development-friendly environment. +- `glee start`: Launches your project for production use. -#### Create `asyncapi.yaml` file and other required directories +#### Setting up `asyncapi.yaml` and Required Directories -Create a yaml file that supports capable of receiving a "hello {name}" message with the protocol as `ws` and the channel name as `hello` the hello API will subscribe to. The operationId property is `onHello` that's the name of function and the payload property is type string publishing to that channel. +Craft an `asyncapi.yaml` file capable of receiving a "hello \{name\}" message via WebSocket (`ws`) protocol on the `hello` channel. Define the operation ID as `onHello`, indicating the function to be called, and set the payload type to string for publishing on that channel. ```yaml +# AsyncAPI Specification for Hello, Glee! asyncapi: 3.0.0 info: title: 'Hello, Glee!' @@ -102,9 +123,6 @@ operations: action: receive channel: $ref: '#/channels/hello' - reply: - channel: - $ref: "#/channels/hello" SendHello: action: send channel: @@ -116,19 +134,21 @@ components: type: string ``` -Create an operation function `onHello.js` inside `myapp/functions`: +Create a function `onHello.js` in `myapp/functions`: ```js export default async function (event) { return { - reply: [{ + send: [{ + server: "websockets", + channel: "hello", payload: `Hello from Glee! You said: "${event.payload}".` }] } } ``` -#### Run the Development Server +#### Launching the Development Server -- Run `npm run dev` to start the development server. -- Connect to `ws://localhost:3000/hello` and send a WebSocket request with a payload e.g. {"john"} +- Execute `npm run dev` to start the server. +- Connect to `ws://localhost:3000/hello` and send a WebSocket request like `{"john"}`. \ No newline at end of file diff --git a/markdown/docs/tools/glee/your-first-glee-app.md b/markdown/docs/tools/glee/your-first-glee-app.md new file mode 100644 index 000000000000..dfdc3051f91c --- /dev/null +++ b/markdown/docs/tools/glee/your-first-glee-app.md @@ -0,0 +1,125 @@ +--- + +title: Building Your First Glee Application +weight: 20 + +--- + +#### Crafting Your API Specification + +Let's start with a straightforward WebSocket API using Glee. Imagine a WebSocket server that receives the current time from a client and responds with a greeting like "good morning", "good evening", or "good night" based on the time provided. + +In Glee, which is a spec-first framework, the journey begins with defining your API specification. If you're unfamiliar with what an API spec is or how to create one, we recommend going through the tutorials available [here](https://www.asyncapi.com/docs/tutorials) before proceeding. For this project, here's how we define our API: + +```yaml +asyncapi: 3.0.0 +info: + title: Greet Bot + version: 1.0.0 +servers: + websockets: + host: localhost:3000 + protocol: ws +channels: + greet: + address: /greet + messages: + greet: + payload: + type: string + time: + payload: + type: object + properties: + currentTime: + type: number + name: + type: string +operations: + receiveTime: + action: receive + channel: + $ref: '#/channels/greet' + messages: + - $ref: "#/channels/greet/messages/time" + sendGreet: + action: send + channel: + $ref: '#/channels/greet' + messages: + - $ref: "#/channels/greet/messages/greet" +``` + +Store this in a file named `asyncapi.yml`. + +This spec shows that our app can perform two operations on a single channel: receiving time and sending a greeting. We've used JSON Schema to define what `time` and `greet` messages look like. + +Note the `operations` section. It's essential in Glee, linking your business logic to your spec. For instance, `receiveTime` is the function invoked when the `/greet` channel receives a message. + +> Tip: Including a `send` operation isn't mandatory but is recommended for validating outgoing messages against your spec. + +### Initiating Your Glee Project + +For ease and efficiency, start your Glee app with our CLI, which automates the setup. In your `asyncapi.yml` file's directory, execute: `asyncapi new glee --name GreetBot --file asyncapi.yml` + +It will prompt you with a list of server names and let you select which servers you want Glee to create a server for. select the `websockets` and continue. + +Then, navigate to your application's folder and install the dependencies: + +```shell +cd GreetBot +npm install --ignore-scripts +``` + +You now have a Glee app scaffolded and ready for development. + +#### Implementing the Operation Function + +Navigate to `functions/receiveTime.js` and input the logic to analyze the time and generate the appropriate response. + +```ts +import { GleeFunction } from '@asyncapi/glee'; + +const receiveTime: GleeFunction = async (event) => { + const { name, time } = event.payload; + const t = new Date(time); + const curHr = t.getHours(); + let response = ''; + if (curHr < 12) { + response = `Good Morning, ${name}!`; + } else if (curHr < 18) { + response = `Good Afternoon, ${name}!`; + } else { + response = `Good Evening, ${name}!`; + } + return { + send: [ + { + server: 'websockets', + channel: 'greet', + payload: response, + }, + ], + }; +}; + +export default receiveTime; +``` + +> Note: Want to know more about functions? [Click Here!](./function-lifecycle-events.md) + +Each file in the `functions` directory is a handler where you can craft your business logic. Every handler should export an asynchronous function that takes an `event` parameter, giving you access to the payload and server details. + +#### Running and Evaluating Your App + +To launch and test the app's functionality, follow these steps: + +Run your Glee application using: + +``` +npm run dev +# or +npm run start +``` + +To test, open a WebSocket connection to `ws://localhost:3000/greet` in Postman and send `{"name":"John","time":"1567906535"}`. Watch as your Glee app responds appropriately. \ No newline at end of file From 0a4bf2faf1b9e3c7174236bb2e7cc68d1e2c6f02 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Tue, 3 Sep 2024 08:29:34 +0200 Subject: [PATCH 45/69] docs(cli): update latest cli documentation (#3189) --- markdown/docs/tools/cli/metrics_collection.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/markdown/docs/tools/cli/metrics_collection.md b/markdown/docs/tools/cli/metrics_collection.md index 3e89f6bddc65..cd9a4c6f7770 100644 --- a/markdown/docs/tools/cli/metrics_collection.md +++ b/markdown/docs/tools/cli/metrics_collection.md @@ -1,3 +1,8 @@ +--- +title: 'Metrics Collection' +weight: 60 +--- + # Metrics collection guideline AsyncAPI **anonymously** tracks command executions to improve the specification and tools, ensuring no sensitive data reaches our servers. It aids in comprehending how AsyncAPI tools are used and adopted, facilitating ongoing improvements to our specifications and tools. From dd0a97746de89d1a434335045739de8e1174f39e Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Tue, 3 Sep 2024 10:19:26 +0200 Subject: [PATCH 46/69] chore: update finance report for june-july-august (#3164) Co-authored-by: asyncapi-bot --- config/finance/2024/Expenses.yml | 47 +++++++++++++++++++++++++++- config/finance/2024/ExpensesLink.yml | 5 ++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/config/finance/2024/Expenses.yml b/config/finance/2024/Expenses.yml index 66e12fda9a57..1467852bbb50 100644 --- a/config/finance/2024/Expenses.yml +++ b/config/finance/2024/Expenses.yml @@ -10,6 +10,8 @@ January: - Category: AsyncAPI Conf on Tour 2023 Amount: '318.98' February: + - Category: Bounty Program + Amount: '400.00' - Category: JSON Schema Sponsorship Amount: '250.00' - Category: Community Manager @@ -50,4 +52,47 @@ May: - Category: Mentorship Program 2023 Amount: '825.00' - Category: Swag Store - Amount: '526.51' \ No newline at end of file + Amount: '526.51' + - Category: Bounty Program + Amount: '800.00' + - Category: Community Marketing Specialist + Amount: '1000.00' +June: + - Category: JSON Schema Sponsorship + Amount: '250.00' + - Category: Community Manager + Amount: '2000.00' + - Category: Community Marketing Specialist + Amount: '2000.00' + - Category: Bounty Program + Amount: '800.00' + - Category: Swag Store + Amount: '526.51' +July: + - Category: JSON Schema Sponsorship + Amount: '250.00' + - Category: Community Manager + Amount: '2000.00' + - Category: Bounty Program + Amount: '1000.00' + - Category: Swag Store + Amount: '526.51' + - Category: Community Marketing Specialist + Amount: '2000.00' + - Category: AsyncAPI Conf on Tour 2024 + Amount: '2083.41' +August: + - Category: JSON Schema Sponsorship + Amount: '250.00' + - Category: Community Manager + Amount: '2000.00' + - Category: Bounty Program + Amount: '1600.00' + - Category: Swag Store + Amount: '2556.42' + - Category: Community Marketing Specialist + Amount: '2000.00' + - Category: 3rd Party Services + Amount: '1354.35' + - Category: AsyncAPI Conf on Tour 2024 + Amount: '1384.70' \ No newline at end of file diff --git a/config/finance/2024/ExpensesLink.yml b/config/finance/2024/ExpensesLink.yml index 449c60ded4b8..97c0c087209f 100644 --- a/config/finance/2024/ExpensesLink.yml +++ b/config/finance/2024/ExpensesLink.yml @@ -26,4 +26,7 @@ link: "https://github.com/orgs/asyncapi/discussions/689" - category: "JSON Schema Sponsorship" - link: "https://github.com/orgs/asyncapi/discussions/1017" \ No newline at end of file + link: "https://github.com/orgs/asyncapi/discussions/1017" + +- category: "Community Marketing Specialist" + link: "https://github.com/orgs/asyncapi/discussions/1176" \ No newline at end of file From 9145e2013ccb64abc75de80e1ca844ed1e707d32 Mon Sep 17 00:00:00 2001 From: V Thulisile Sibanda <66913810+thulieblack@users.noreply.github.com> Date: Wed, 4 Sep 2024 08:06:28 +0200 Subject: [PATCH 47/69] feat: update Slack Link (#3194) --- public/_redirects | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/_redirects b/public/_redirects index baf7f222666c..1af1518b86eb 100644 --- a/public/_redirects +++ b/public/_redirects @@ -53,7 +53,7 @@ https://www.asyncapi.io/* https://www.asyncapi.com/:splat 301! /asyncapi-react https://asyncapi.github.io/asyncapi-react 301! # Slack -/slack-invite https://join.slack.com/t/asyncapi/shared_invite/zt-2h8eqemvq-Iq96dTS_zrrNjKt6C8hjvQ 302! +/slack-invite https://join.slack.com/t/asyncapi/shared_invite/zt-2q37kkext-RlUOZvxwtCcSHdLhZJByQw 302! # Central Maven repository verification /OSSRH-63280 https://github.com/asyncapi/java-asyncapi @@ -69,4 +69,4 @@ https://www.asyncapi.io/* https://www.asyncapi.com/:splat 301! # Additional redirection /community/meetings /community/events 301! -/cheatsheet https://github.com/asyncapi/website/tree/master/cheatsheet 302! \ No newline at end of file +/cheatsheet https://github.com/asyncapi/website/tree/master/cheatsheet 302! From 377a3926bcbac4e09cb2b073da1764819a28c819 Mon Sep 17 00:00:00 2001 From: V Thulisile Sibanda <66913810+thulieblack@users.noreply.github.com> Date: Thu, 5 Sep 2024 07:23:32 +0200 Subject: [PATCH 48/69] chore(blog): add august summary (#3166) Co-authored-by: Lukasz Gornicki --- markdown/blog/2024-august-summary.md | 100 ++++++++++++++++++ markdown/blog/2024-july-summary.md | 1 - .../2024-blog-banner/blog-banner-august.webp | Bin 0 -> 133150 bytes 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 markdown/blog/2024-august-summary.md create mode 100644 public/img/posts/2024-blog-banner/blog-banner-august.webp diff --git a/markdown/blog/2024-august-summary.md b/markdown/blog/2024-august-summary.md new file mode 100644 index 000000000000..477210de06a1 --- /dev/null +++ b/markdown/blog/2024-august-summary.md @@ -0,0 +1,100 @@ +--- +title: "Community Update: August 2024" +date: 2024-09-03T06:00:00+01:00 +type: Communication +tags: + - Project Status +cover: /img/posts/2024-blog-banner/blog-banner-august.webp +authors: + - name: Thulisile Sibanda + photo: /img/avatars/thulieblack.webp + link: https://www.linkedin.com/in/v-thulisile-sibanda/ + byline: AsyncAPI Community Manager +excerpt: 'August Community Update' +featured: true +--- + +The summer holidays are almost over, but the community hasn't slowed down. We've been receiving great news, and I'm thrilled to share that our list of sponsors is growing. + +We're excited to welcome [HDI Global](https://www.hdi.global/) as our first end-user non-software vendor company as our Silver Sponsor. We're grateful to [Manuel Ottlik](https://www.linkedin.com/in/manuelottlik) for helping us secure this sponsorship. + + +hdiglobal + + +Additionally, we'd like to welcome [Route4Me](https://www.route4me.com/) as our Silver Sponsor for the initiative. We're thankful for their support. + + +route4me + + +Finally, we've received the transfers from [Postman](https://www.postman.com/). As promised, they've elevated their support from Gold to Platinum level and become a Platinum sponsor for the [AsyncAPI Conf on Tour](https://conference.asyncapi.com/). They've also allocated an extra $10,000 to further initiatives within the AsyncAPI ecosystem. + +We're truly grateful for the support we're receiving to drive the vision and the initiative forward. + + +## AsyncAPI Conference's + +We are excited that [our 2nd AsyncAPI Conference will be in London on September 18th](https://conference.asyncapi.com/venue/London). The event will feature a fantastic lineup of speakers and a jam-packed schedule. +As promised, we have some free community tickets for the conference; [please visit the AsyncAPI Conference website and click the register button](https://conference.asyncapi.com/). + +**Note**: *When getting your tickets, please choose quantity one (1) of the Regular ticket and click the blue continue button to get your free ticket.* + +If you are unable to attend our London conference, we will also be heading to the Paris conference on December 5th. [Our Call for Proposals is still open](https://conference.asyncapi.com/venue/Paris), and we encourage everyone to submit a proposal and share their expertise with the community. + + +Additionally, [the Call for Proposals for our Online Conference is open](https://conference.asyncapi.com/venue/Online). Don't miss this opportunity to participate in and join us for this virtual event. +The deadline for proposal submissions will be on the 20th of September. + +## Spec x Tooling + +### OpenAPI to AsyncAPI Converter +We love seeing the impact of the [AsyncAPI Bounty Program](https://github.com/orgs/asyncapi/projects/36/?pane=info) and how it solves community needs. One recently completed project introduced an OpenAPI 3.0 to AsyncAPI 3.0 convertor. The converter enables the easy transition of OpenAPI 3.0 documents to AsyncAPI v3. Thanks to [Mintu Gogoi's](https://github.com/Gmin2) effort and contribution, as well as [Jonas Lagoni](https://github.com/jonaslagoni), for reviewing, providing feedback, and providing guidance until the issue completion. + +For more details about the converter, [please check the AsyncAPI converter readme](https://github.com/asyncapi/converter-js/blob/master/README.md#openapi-30-to-asyncapi-30-conversion). + +### AsyncAPI Developer Network + +The initial version of the [AsyncAPI Developer Network](https://asyncapi-developer-portal.netlify.app/) is out. Here, you can access all the details about the specification and its components, including bindings, schemes, and security. The main goal is to gather existing and new guidelines on the use cases of the AsyncAPI specification in one place. +Kudos to [Pavel Bodiachevskii](https://www.linkedin.com/in/pavel-bo/) for his outstanding work. + +Be sure to visit the [AsyncAPI Developer Network portal](https://asyncapi-developer-portal.netlify.app/) for additional information. + +### Kotlin-AsyncAPI +The donation for [Kotlin-AsyncAPI](https://github.com/asyncapi/kotlin-asyncapi) is complete, and the repository has successfully migrated under the AsyncAPI organization. Many thanks to [Lorenz Simon](https://github.com/lorenzsimon), and we are pleased to welcome him as a new AsyncAPI maintainer as well. + +### AsyncAPI Maintainers List + +Thanks to our bounty program and support from [Mateusz Szostok](https://github.com/mszostok), we're now maintaining the list of all AsyncAPI Maintainers in one place in an automated way. +The functionality is based on GitHub feature called `CODEOWNERS`. Each repository contains such a file and is the truth source for us. The new automation keeps an eye on these files and updates [MAINTAINERS.yaml](https://github.com/asyncapi/community/blob/master/MAINTAINERS.yaml). In such a large organization, it is hard to follow all the changes in ownership of certain repositories manually. + +Thanks to the automation, we also know that the current number of maintainers in the AsyncAPI Initiative is **72**. + +## AsyncAPI Mentorship Program +The planning for the 3rd cohort of the AsyncAPI Mentorship Program is already underway. In this edition, we prioritize projects that promote maintainership, meaning project ideas should align with our goals and pave the way for contributors to evolve into maintainers. + +If you have an exciting project idea that should be part of the program, please share it in the [Mentorship Program 2024 discussion](https://github.com/orgs/asyncapi/discussions/1361). If you want to participate as a mentor, [please read more in the AsyncAPI Call for Mentors discussion](https://github.com/orgs/asyncapi/discussions/1350). + +## Case Studies +We're thrilled to announce the publication of our second case study. The HDI Global brand provides life and property/casualty insurance services in Germany and internationally. They serve individuals and corporate clients and have offered industrial insurance since 2016. + +For more in-depth information, [explore the HDI Case Study on our website](https://www.asyncapi.com/casestudies/hdiglobal). + + +## AsyncAPI Ambassador + +We are pleased to welcome [Manuel Ottlik](https://www.linkedin.com/in/manuelottlik), Product Owner of the Integration Platform at HDI Global SE, as our [AsyncAPI Ambassador](https://www.asyncapi.com/community/ambassadors/manuelottlik). + + + +If you want to join the program or learn more about our current ambassadors, please visit our [AsyncAPI Ambassador Program Page](https://www.asyncapi.com/community/ambassadors). + +## Coming in September +- **AACoT'24 London** - [The London Edition will take place on September 18th and 19th](https://conference.asyncapi.com/venue/London). Be sure to get your tickets. See you there!! +- **AsyncAPI Community Updates Newsletter** - The September Edition issue will arrive in your inbox on the 5th. [Ensure you subscribe to the AsyncAPI Newsletter](https://www.asyncapi.com/newsletter); you don't want to miss out. \ No newline at end of file diff --git a/markdown/blog/2024-july-summary.md b/markdown/blog/2024-july-summary.md index 04e5a842d64e..07bedd27b559 100644 --- a/markdown/blog/2024-july-summary.md +++ b/markdown/blog/2024-july-summary.md @@ -11,7 +11,6 @@ authors: link: https://www.linkedin.com/in/v-thulisile-sibanda/ byline: AsyncAPI Community Manager excerpt: 'July Community Update' -featured: true --- It's been a month since our partnership with Postman ended, and some of our maintainers at AsyncAPI lost their jobs. However, we are proud of sponsors like IBM, who continue to support open source and the Initiative. Please take a moment to read [Salma Saeed's](https://www.linkedin.com/in/s-saeed/) article [on how IBM is supporting AsyncAPI in breaking the boundaries of Event-Driven Architecture](https://www.ibm.com/blog/ibm-continues-to-support-opensource-asyncapi-in-breaking-the-boundaries-of-event-driven-architectures). diff --git a/public/img/posts/2024-blog-banner/blog-banner-august.webp b/public/img/posts/2024-blog-banner/blog-banner-august.webp new file mode 100644 index 0000000000000000000000000000000000000000..845c4fc951c6225fd7c339737e802af4e96e65b0 GIT binary patch literal 133150 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
ujrG2;K1^Ts)hFuNW^D^j@ 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 literal 0 HcmV?d00001 From dfee7d003e0a0598b0d0feaf7ffea09c628087a5 Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela <90895835+vishvamsinh28@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:46:20 +0530 Subject: [PATCH 49/69] feat: added tests for build-meetings.js (#3076) Co-authored-by: Ansh Goyal --- scripts/build-meetings.js | 46 ++++++++----- tests/build-meetings.test.js | 117 +++++++++++++++++++++++++++++++++ tests/fixtures/meetingsData.js | 25 +++++++ 3 files changed, 172 insertions(+), 16 deletions(-) create mode 100644 tests/build-meetings.test.js create mode 100644 tests/fixtures/meetingsData.js diff --git a/scripts/build-meetings.js b/scripts/build-meetings.js index b63a20fd4873..ee95803d9d44 100644 --- a/scripts/build-meetings.js +++ b/scripts/build-meetings.js @@ -2,13 +2,22 @@ const { writeFileSync } = require('fs'); const { resolve } = require('path'); const { google } = require('googleapis'); -async function buildMeetings() { - const auth = new google.auth.GoogleAuth({ - scopes: ['https://www.googleapis.com/auth/calendar'], - credentials: JSON.parse(process.env.CALENDAR_SERVICE_ACCOUNT), - }); +async function buildMeetings(writePath) { + let auth; + let calendar; + + try { + auth = new google.auth.GoogleAuth({ + scopes: ['https://www.googleapis.com/auth/calendar'], + credentials: process.env.CALENDAR_SERVICE_ACCOUNT ? JSON.parse(process.env.CALENDAR_SERVICE_ACCOUNT) : undefined, + }); + + calendar = google.calendar({ version: 'v3', auth }); + + } catch (err) { + throw new Error(`Authentication failed: ${err.message}`); + } - const calendar = google.calendar({ version: 'v3', auth }); let eventsItems; try { @@ -20,8 +29,9 @@ async function buildMeetings() { const timeMax = new Date( Date.parse(currentTime) + 30 * 24 * 60 * 60 * 1000 ).toISOString(); + const eventsList = await calendar.events.list({ - calendarId: process.env.CALENDAR_ID, + calendarId: process.env.CALENDAR_ID, timeMax: timeMax, timeMin: timeMin, }); @@ -40,14 +50,18 @@ async function buildMeetings() { }); const eventsForHuman = JSON.stringify(eventsItems, null, ' '); - // console.log('The following events got fetched', eventsForHuman); - - writeFileSync( - resolve(__dirname, '../config', 'meetings.json'), - eventsForHuman - ); - } catch (e) { - console.error(e); + console.log('The following events got fetched', eventsForHuman); + + writeFileSync(writePath, eventsForHuman); + + } catch (err) { + throw new Error(`Failed to fetch or process events: ${err.message}`); } } -buildMeetings(); + +/* istanbul ignore next */ +if (require.main === module) { + buildMeetings(resolve(__dirname, '../config', 'meetings.json')); +} + +module.exports = { buildMeetings }; diff --git a/tests/build-meetings.test.js b/tests/build-meetings.test.js new file mode 100644 index 000000000000..bd4d9db5b1ec --- /dev/null +++ b/tests/build-meetings.test.js @@ -0,0 +1,117 @@ +const { google } = require('googleapis'); +const path = require("path"); +const { readFileSync, mkdirSync, rmSync } = require('fs'); +const { buildMeetings } = require('../scripts/build-meetings'); +const { mockEvents, expectedContent } = require('../tests/fixtures/meetingsData'); + +jest.mock('googleapis', () => { + const events = { + list: jest.fn(), + }; + const calendar = { + events, + }; + const google = { + calendar: jest.fn(() => calendar), + auth: { + GoogleAuth: jest.fn(() => ({ + getClient: jest.fn(), + })), + }, + }; + return { google }; +}); + +describe('buildMeetings', () => { + const testDir = path.join(__dirname, 'testCache'); + const outputFilePath = path.join(testDir, 'meetings.json'); + + beforeEach(() => { + jest.clearAllMocks(); + process.env.CALENDAR_SERVICE_ACCOUNT = JSON.stringify({ key: 'test_key' }); + process.env.CALENDAR_ID = 'test_calendar_id'; + + mkdirSync(testDir, { recursive: true }); + }); + + afterEach(() => { + rmSync(testDir, { recursive: true, force: true }); + }); + + it('should fetch events, process them, and write to a file', async () => { + google.calendar().events.list.mockResolvedValue({ data: { items: mockEvents } }); + + await buildMeetings(outputFilePath); + + expect(google.auth.GoogleAuth).toHaveBeenCalledWith({ + scopes: ['https://www.googleapis.com/auth/calendar'], + credentials: { key: 'test_key' }, + }); + expect(google.calendar).toHaveBeenCalled(); + expect(google.calendar().events.list).toHaveBeenCalledWith({ + calendarId: 'test_calendar_id', + timeMax: expect.any(String), + timeMin: expect.any(String), + }); + + const fileContent = readFileSync(outputFilePath, 'utf8'); + const parsedContent = JSON.parse(fileContent); + + expect(parsedContent).toEqual(expectedContent); + }); + + it('should throw an error if the Google API call fails', async () => { + google.calendar().events.list.mockRejectedValue(new Error('Google API error')); + + try { + await buildMeetings(outputFilePath) + } catch (err) { + expect(err.message).toContain('Google API error'); + } + }); + + it('should handle undefined CALENDAR_SERVICE_ACCOUNT', async () => { + delete process.env.CALENDAR_SERVICE_ACCOUNT; + + google.calendar().events.list.mockResolvedValue({ data: { items: [] } }); + + await buildMeetings(outputFilePath); + + expect(google.auth.GoogleAuth).toHaveBeenCalledWith({ + scopes: ['https://www.googleapis.com/auth/calendar'], + credentials: undefined, + }); + + const fileContent = readFileSync(outputFilePath, 'utf8'); + expect(fileContent).toBe('[]'); + }); + + it('should throw an error if authentication fails', async () => { + google.auth.GoogleAuth.mockImplementation(() => { + throw new Error('Authentication failed'); + }); + + try { + await buildMeetings(outputFilePath) + } catch (err) { + expect(err.message).toContain('Authentication failed') + } + }); + + it('should handle file write errors', async () => { + google.auth.GoogleAuth.mockImplementation(() => ({ + getClient: jest.fn(), + })); + + google.calendar().events.list.mockResolvedValue({ data: { items: mockEvents } }); + + const invalidPath = '/root/invalid_dir/meetings.json'; + + try { + await buildMeetings(invalidPath); + } catch (err) { + expect(err.message).toMatch(/ENOENT|EACCES/); + } + }); + +}); diff --git a/tests/fixtures/meetingsData.js b/tests/fixtures/meetingsData.js new file mode 100644 index 000000000000..fa55b7695483 --- /dev/null +++ b/tests/fixtures/meetingsData.js @@ -0,0 +1,25 @@ +const mockEvents = [ + { + summary: 'Community Meeting', + htmlLink: 'https://www.google.com/calendar/event?eid=example', + extendedProperties: { + private: { + ISSUE_ID: '123', + BANNER: 'https://example.com/banner.jpg', + }, + }, + start: { dateTime: '2024-02-20T16:00:00.000Z' }, + }, +]; + +const expectedContent = [ + { + banner: "https://example.com/banner.jpg", + calLink: "https://www.google.com/calendar/event?eid=example", + date: "2024-02-20T16:00:00.000Z", + title: "Community Meeting", + url: "https://github.com/asyncapi/community/issues/123" + }, +]; + +module.exports = { mockEvents, expectedContent } From 5902b0eadbfcb44cd3bc1dd7ef60a78d702ee076 Mon Sep 17 00:00:00 2001 From: asyncapi-bot Date: Fri, 6 Sep 2024 09:12:13 +0200 Subject: [PATCH 50/69] docs(cli): update latest cli documentation (#3195) --- markdown/docs/tools/cli/usage.md | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/markdown/docs/tools/cli/usage.md b/markdown/docs/tools/cli/usage.md index ed4b97ff18ee..c11e3b5492e4 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.3.1 linux-x64 node-v18.20.4 +@asyncapi/cli/2.3.2 linux-x64 node-v18.20.4 $ asyncapi --help [COMMAND] USAGE $ asyncapi COMMAND @@ -99,7 +99,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.3.1/src/commands/bundle.ts)_ +_See code: [src/commands/bundle.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/bundle.ts)_ ## `asyncapi config` @@ -113,7 +113,7 @@ DESCRIPTION CLI config settings ``` -_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/index.ts)_ +_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/index.ts)_ ## `asyncapi config analytics` @@ -133,7 +133,7 @@ DESCRIPTION Enable or disable analytics for metrics collection ``` -_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/analytics.ts)_ +_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/analytics.ts)_ ## `asyncapi config context` @@ -147,7 +147,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.3.1/src/commands/config/context/index.ts)_ +_See code: [src/commands/config/context/index.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/index.ts)_ ## `asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH` @@ -169,7 +169,7 @@ DESCRIPTION Add a context to the store ``` -_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/context/add.ts)_ +_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/add.ts)_ ## `asyncapi config context current` @@ -186,7 +186,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.3.1/src/commands/config/context/current.ts)_ +_See code: [src/commands/config/context/current.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/current.ts)_ ## `asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH` @@ -207,7 +207,7 @@ DESCRIPTION Edit a context in the store ``` -_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/context/edit.ts)_ +_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/edit.ts)_ ## `asyncapi config context init [CONTEXT-FILE-PATH]` @@ -230,7 +230,7 @@ DESCRIPTION Initialize context ``` -_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/context/init.ts)_ +_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/init.ts)_ ## `asyncapi config context list` @@ -247,7 +247,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.3.1/src/commands/config/context/list.ts)_ +_See code: [src/commands/config/context/list.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/list.ts)_ ## `asyncapi config context remove CONTEXT-NAME` @@ -267,7 +267,7 @@ DESCRIPTION Delete a context from the store ``` -_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/context/remove.ts)_ +_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/remove.ts)_ ## `asyncapi config context use CONTEXT-NAME` @@ -287,7 +287,7 @@ DESCRIPTION Set a context as current ``` -_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/context/use.ts)_ +_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/context/use.ts)_ ## `asyncapi config versions` @@ -304,7 +304,7 @@ DESCRIPTION Show versions of AsyncAPI tools used ``` -_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/config/versions.ts)_ +_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/config/versions.ts)_ ## `asyncapi convert [SPEC-FILE]` @@ -326,7 +326,7 @@ DESCRIPTION Convert asyncapi documents older to newer versions ``` -_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/convert.ts)_ +_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/convert.ts)_ ## `asyncapi diff OLD NEW` @@ -366,7 +366,7 @@ DESCRIPTION Find diff between two asyncapi files ``` -_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/diff.ts)_ +_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/diff.ts)_ ## `asyncapi generate` @@ -380,7 +380,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.3.1/src/commands/generate/index.ts)_ +_See code: [src/commands/generate/index.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/generate/index.ts)_ ## `asyncapi generate fromTemplate ASYNCAPI TEMPLATE` @@ -424,7 +424,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.3.1/src/commands/generate/fromTemplate.ts)_ +_See code: [src/commands/generate/fromTemplate.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/generate/fromTemplate.ts)_ ## `asyncapi generate models LANGUAGE FILE` @@ -495,7 +495,7 @@ DESCRIPTION Generates typed models ``` -_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/generate/models.ts)_ +_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/generate/models.ts)_ ## `asyncapi new` @@ -553,7 +553,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.3.1/src/commands/new/index.ts)_ +_See code: [src/commands/new/index.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/new/index.ts)_ ## `asyncapi new file` @@ -611,7 +611,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.3.1/src/commands/new/file.ts)_ +_See code: [src/commands/new/file.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/new/file.ts)_ ## `asyncapi new glee` @@ -633,7 +633,7 @@ DESCRIPTION Creates a new Glee project ``` -_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/new/glee.ts)_ +_See code: [src/commands/new/glee.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/new/glee.ts)_ ## `asyncapi new template` @@ -657,7 +657,7 @@ DESCRIPTION Creates a new template ``` -_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/new/template.ts)_ +_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/new/template.ts)_ ## `asyncapi optimize [SPEC-FILE]` @@ -699,7 +699,7 @@ EXAMPLES $ asyncapi optimize ./asyncapi.yaml --ignore=schema ``` -_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/optimize.ts)_ +_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/optimize.ts)_ ## `asyncapi start` @@ -708,7 +708,7 @@ USAGE $ asyncapi start ``` -_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/start/index.ts)_ +_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/start/index.ts)_ ## `asyncapi start studio` @@ -727,7 +727,7 @@ DESCRIPTION starts a new local instance of Studio ``` -_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/start/studio.ts)_ +_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/start/studio.ts)_ ## `asyncapi validate [SPEC-FILE]` @@ -757,5 +757,5 @@ DESCRIPTION validate asyncapi file ``` -_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/validate.ts)_ +_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.3.2/src/commands/validate.ts)_ From 4e364821f7100f594e0c5144cc5f64d1d1d8eba8 Mon Sep 17 00:00:00 2001 From: Sambhav Gupta <81870866+sambhavgupta0705@users.noreply.github.com> Date: Sat, 7 Sep 2024 12:07:39 +0530 Subject: [PATCH 51/69] fix: nexti18nSetup for OG tags error (#3111) * initial commit * fixes * package.json changes * fixing local host url * foixed caseStudies * home page fix * fixed GH button * fixed linting * fixed linting * fixed githubButton * react-i18next * backticks fix * updated package-lock.json * removed lint errors * package-lock.json * package-lock.json changes * package-lock.json changes2 * rtesolving merge conflicts * resolving merge conflicts * resolving merge conflicts * resolving conflicts * package changes * fix package-lock * fix mdx * fix: date warnings * fixed lint errors * fix * temporary commit * temporary commit * temporary commit * revert breaking changes * space fixes * test * test * featured blog post file changes Signed-off-by: Sambhav Gupta * lint error fix Signed-off-by: Sambhav Gupta --------- Signed-off-by: Sambhav Gupta Co-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com> Co-authored-by: akshatnema Co-authored-by: JeelRajodiya --- components/CaseStudyCard.tsx | 2 +- components/Head.tsx | 2 +- components/MDX/MDX.tsx | 309 +++++++++--------- components/MDX/MDXTable.tsx | 20 ++ components/NewsletterSubscribe.tsx | 2 +- components/dashboard/Header.tsx | 2 +- components/link.tsx | 3 +- components/navigation/NavBar.tsx | 5 +- components/newsroom/FeaturedBlogPost.tsx | 4 +- ...capi-and-apicurio-for-asynchronous-apis.md | 6 +- ...itectural-style-to-suplement-api-design.md | 7 +- markdown/blog/status-update-27-20.md | 2 +- markdown/blog/websocket-part2.md | 2 +- markdown/docs/tools/cli/context.md | 2 +- mdx-components.tsx | 16 + next-i18next-static-site.config.js | 8 - next-i18next.config.js | 10 + next.config.mjs | 1 + package-lock.json | 119 ++++++- package.json | 5 +- pages/[lang]/index.tsx | 30 +- pages/[lang]/tools/cli.tsx | 36 +- pages/_app.tsx | 60 ++-- pages/_document.tsx | 7 +- pages/casestudies/[id].tsx | 10 +- pages/index.tsx | 7 +- pages/tools/cli.tsx | 1 + pages/tools/generator.tsx | 6 +- pages/tools/github-actions.tsx | 6 +- pages/tools/modelina.tsx | 1 + pages/tools/parsers.tsx | 6 +- {locales => public/locales}/de/common.json | 0 .../locales}/de/landing-page.json | 0 {locales => public/locales}/de/tools.json | 0 {locales => public/locales}/en/common.json | 0 .../locales}/en/landing-page.json | 0 {locales => public/locales}/en/tools.json | 0 utils/browserLanguageDetector.ts | 45 --- utils/getStatic.ts | 57 ++++ utils/i18n.ts | 21 ++ utils/i18n.tsx | 211 ------------ utils/i18nPaths.ts | 16 - utils/languageDetector.ts | 8 + utils/locales.ts | 21 -- utils/redirect.ts | 52 +++ utils/staticHelpers.ts | 6 +- 46 files changed, 545 insertions(+), 589 deletions(-) create mode 100644 mdx-components.tsx delete mode 100644 next-i18next-static-site.config.js create mode 100644 next-i18next.config.js rename {locales => public/locales}/de/common.json (100%) rename {locales => public/locales}/de/landing-page.json (100%) rename {locales => public/locales}/de/tools.json (100%) rename {locales => public/locales}/en/common.json (100%) rename {locales => public/locales}/en/landing-page.json (100%) rename {locales => public/locales}/en/tools.json (100%) delete mode 100644 utils/browserLanguageDetector.ts create mode 100644 utils/getStatic.ts create mode 100644 utils/i18n.ts delete mode 100644 utils/i18n.tsx delete mode 100644 utils/i18nPaths.ts create mode 100644 utils/languageDetector.ts delete mode 100644 utils/locales.ts create mode 100644 utils/redirect.ts diff --git a/components/CaseStudyCard.tsx b/components/CaseStudyCard.tsx index 241e9b5aa84b..1642b8244506 100644 --- a/components/CaseStudyCard.tsx +++ b/components/CaseStudyCard.tsx @@ -19,7 +19,7 @@ export default function CaseStudyCard({ studies = [] }: ICaseStudyCardProps) { return (

{studies.map((study, index) => ( - +
) => ( -

- ), - h2: (props: React.HTMLProps) => ( -

- ), - h3: (props: React.HTMLProps) => ( -

- ), - h4: (props: React.HTMLProps) => ( -

- ), - h5: (props: React.HTMLProps) => ( -
- ), - h6: (props: React.HTMLProps) => ( -
- ), - blockquote: (props: React.HTMLProps) => ( -
- ), - p: (props: React.HTMLProps) => ( -

- ), - strong: (props: React.HTMLProps) => ( - - ), - a: (props: React.HTMLProps) => ( - - ), - ul: (props: React.HTMLProps) => ( -