From 04adfcdd1bd4b8e0cb079a349866328fd9e1798f Mon Sep 17 00:00:00 2001 From: derberg Date: Tue, 30 Aug 2022 16:01:10 +0200 Subject: [PATCH 01/28] feat: case studies content setup --- casestudy_template.yml | 66 ++++++++++++++++++++++++++ public/casestudies/adeo.yml | 94 +++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 casestudy_template.yml create mode 100644 public/casestudies/adeo.yml diff --git a/casestudy_template.yml b/casestudy_template.yml new file mode 100644 index 000000000000..de5819c69774 --- /dev/null +++ b/casestudy_template.yml @@ -0,0 +1,66 @@ +company: + name: #Company name. + description: #Shor description of most important things people should know about the company. + customers: #Amount of customers to indicate the impact that the company has. + industry: #Is it retail? software development? put the same value that company has on their LinkedIn page. + revenue: #Specify company earnings to indicate to case study reader how critical online operations are. + website: #Link to main website. + logo: #Link to logo of the company in SVG format. +challenges: | + #Describe want challanges company have, that faced them towards AsyncAPI. There is no limit here. Use [CommonMark](https://commonmark.org/). +solution: | + #Describe the solution for the challange and what role AsyncAPI played in it. +technical: + language: + - #Provide a list of programming lanugages people using AsyncAPI work on + frameworks: + - #Specify what frameworks they use, maybe Spring in Java or Nest.js in JavaScript + protocol: + - #What protocols are used by the company in relation to AsyncAPI + broker: | + #Provide some details about the broker that is used at the company and what is the setup, size, like of example cluster size, number of topics (in case of Kafka for example). + testing: | + #Explain how testing of the solution is done. How you ensure API works as expected after changes, that users are not affected. For example how do you know who will be affected if you stop sending some message? Provide examples if possible. + architecture: | + #Explain the setup of the EDA architecture. Perfect woudl be if you could point what [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) you applied. Provide examples if possible. +codegen: | + #Explain if you use code generation, what you generate and how. Provide examples if possible. +schemas: + description: #What spec and version do you use, is it JSON Schema? Avro? What version? + storage: #Where do you store your schemas + registry: #Are you using schema registry solution? which one? + versioning: #How do you handle versioning of schemas? + validation: #How do you handle events payload validation against schemas? +asyncapi: + usecase: | + #Even though this section might be a kind of replication of what was explained in challenges/solution, please describe in short what specific use case you found for AsyncAPI and how you implement it. + versions: + - #Specify a list of versions of AsyncAPI that you use. It can be a list, some teams might use different versions. + storage: | + #Describe where you store AsyncAPI files and who maintains them on daily basis + editing: + #Specify how you edit AsyncAPI files, where do you edit them. Be very specific on IDEs and plugins names, if used. + audience: + internal: true + external: false + documentation: | + #Explain processes around documentation generated with AsyncAPI. How you do it, what AsyncAPI fields are critical for you. Provide examples if possible. + bindings: + kafka: | + #Specify what bindings are used by their name and provide description on what level you use bindings. Provide examples if possible. + tools: #specify what AsyncAPI tools you use, as detailed as possible + generator: + templates: + '@asyncapi/html': + parameters: + - sidebarOrganization=byTags + - version + parser: + plugins: + - avro-schema-parser +contact: | + #Let us know who we can contact you, some generic email or just a name and link to some social media. We need this to be able tools: + - validate long term if case study is still validate + - ping you when we know we plan to change some parts of AsyncAPI that you are already using +example: | + #Provide full example of AsyncAPI file that you are using on production. \ No newline at end of file diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml new file mode 100644 index 000000000000..4fea8e86b16c --- /dev/null +++ b/public/casestudies/adeo.yml @@ -0,0 +1,94 @@ +company: + name: Adeo Group + description: Owning different brands in retail industry focused on home improvement and DIY markets, like Leroy Merlin. + customers: 500M + industry: Retail + revenue: 25.6B EURO turnover, including 768M EURO online + website: https://www.adeo.com/ + logo: https://www.adeo.com/assets-adeo/themes/adeo-refonte/dist/assets/images/logo-adeo-v3.svg +challenges: | + Cost Component Repository product is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use differen information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. + + Initial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel. + + There was a need for a standart way of describing event-driven architecture. +solution: | + The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for different units and external partners. + + Payloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs. + + Shift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files. +technical: + language: + - Java + frameworks: + - Spring + protocol: + - Kafka + broker: TODO: size of cluster + testing: TODO: how is it done + architecture: | + The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: + - [Request/reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) + Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel. + TODO: Can we have a snippet? + - [Return address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) + Message Header object with `REPLY_TOPIC` header property. Info that needs to be provided by client, so producer knows where to send a response. + TODO: Can we have a snippet? + - [Correlation identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) + Important to follow to be able to identify what request does the respons match. + `REQUEST_ID` in Message Header object, and pointed out with `correlationID` property. + TODO: I could not figure out what role `CORRELATION_ID` is playing. Is it that once producer sends a message to reply channel, then info about request id is not in `REQUEST_ID` but `CORRELATION_ID`. So they both have the same info, but one is for request producer and the other for reply producer? + TODO: Can we have a snippet? + - [DeadLetter channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) + Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. In AsyncAPI for now it is just part of channel description, channel that works as DeadLetter channel. + TODO: Can we have a snippet? +codegen: | + Java models generation. Avro schemas used as a source. +documentation: | + Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. + Publishing is part of CI/CD pipeline for the product using GitLabCI. + + Critical features of AsyncAPI related to documentation: + - `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs + - examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated + - `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI +schemas: + description: Avro 1.9 + storage: Git repository where source code is. + registry: Confluent Schema registry + versioning: TODO: how is it done? + validation: TODO: how is validation done in runtime +asyncapi: + usecase: | + Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation for machine-readable document that describes the API. + The goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company. + versions: + - 2.0.0 TODO: + storage: Git repository where source code is. + maintainers: + Developers + editing: + description: + editors: TODO: + VSCode: + plugins: + audience: + internal: true + external: false + bindings: + kafka: | + Kafka bindings are used on channel, operation and message level. + TODO: but channel bindings that you use in your examples are not in https://github.com/asyncapi/bindings/tree/master/kafka#channel-binding-object + tools: + generator: + templates: + '@asyncapi/html': + parameters: + - sidebarOrganization=byTags + - version + parser: + plugins: + - avro-schema-parser +contact: | + TODO: \ No newline at end of file From 3449015629646acb120353d3d4ce143424bbf85c Mon Sep 17 00:00:00 2001 From: derberg Date: Tue, 30 Aug 2022 16:13:32 +0200 Subject: [PATCH 02/28] anonymous note --- casestudy_template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casestudy_template.yml b/casestudy_template.yml index de5819c69774..01f5eba68aea 100644 --- a/casestudy_template.yml +++ b/casestudy_template.yml @@ -1,4 +1,4 @@ -company: +company: #This is optional, if company wants to provide case study data anonymously, they can definitely do it name: #Company name. description: #Shor description of most important things people should know about the company. customers: #Amount of customers to indicate the impact that the company has. From ec04a3c3fceb87eb79fabafcb157ce48ce47b389 Mon Sep 17 00:00:00 2001 From: derberg Date: Tue, 30 Aug 2022 16:27:22 +0200 Subject: [PATCH 03/28] unify source and template --- casestudy_template.yml | 4 +++- public/casestudies/adeo.yml | 26 ++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/casestudy_template.yml b/casestudy_template.yml index 01f5eba68aea..30c6d865af8f 100644 --- a/casestudy_template.yml +++ b/casestudy_template.yml @@ -40,9 +40,11 @@ asyncapi: #Describe where you store AsyncAPI files and who maintains them on daily basis editing: #Specify how you edit AsyncAPI files, where do you edit them. Be very specific on IDEs and plugins names, if used. - audience: + audience: #Specify if AsyncAPI audience is internal or external or both internal: true external: false + extensions: | + #Explain if you use spec extension and how. Provide examples if possible. documentation: | #Explain processes around documentation generated with AsyncAPI. How you do it, what AsyncAPI fields are critical for you. Provide examples if possible. bindings: diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 4fea8e86b16c..a6b840f1f0c8 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -45,14 +45,6 @@ technical: TODO: Can we have a snippet? codegen: | Java models generation. Avro schemas used as a source. -documentation: | - Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. - Publishing is part of CI/CD pipeline for the product using GitLabCI. - - Critical features of AsyncAPI related to documentation: - - `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs - - examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated - - `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI schemas: description: Avro 1.9 storage: Git repository where source code is. @@ -68,14 +60,19 @@ asyncapi: storage: Git repository where source code is. maintainers: Developers - editing: - description: - editors: TODO: - VSCode: - plugins: + editing: TODO: please provide info audience: internal: true external: false + extensions: TODO: do you use any? + documentation: | + Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. + Publishing is part of CI/CD pipeline for the product using GitLabCI. + + Critical features of AsyncAPI related to documentation: + - `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs + - examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated + - `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI bindings: kafka: | Kafka bindings are used on channel, operation and message level. @@ -91,4 +88,5 @@ asyncapi: plugins: - avro-schema-parser contact: | - TODO: \ No newline at end of file + TODO: +example: TODO: please provide one full example \ No newline at end of file From 6e10ff8b717f65e4d360266d0dd6bacd4133ab3a Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Tue, 6 Sep 2022 09:14:31 +0200 Subject: [PATCH 04/28] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fran Méndez --- public/casestudies/adeo.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index a6b840f1f0c8..e6ddce6c47f3 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -19,13 +19,13 @@ solution: | Shift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files. technical: - language: + languages: - Java frameworks: - Spring - protocol: + protocols: - Kafka - broker: TODO: size of cluster + brokers: TODO: size of cluster testing: TODO: how is it done architecture: | The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: From 5d6373c0a2bec3670751adaf6016c303317897ab Mon Sep 17 00:00:00 2001 From: derberg Date: Tue, 6 Sep 2022 15:51:43 +0200 Subject: [PATCH 05/28] added a bit more clarification from Ludovic --- public/casestudies/adeo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index e6ddce6c47f3..1a5b94291daa 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -13,7 +13,7 @@ challenges: | There was a need for a standart way of describing event-driven architecture. solution: | - The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for different units and external partners. + The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API. Payloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs. @@ -47,7 +47,7 @@ codegen: | Java models generation. Avro schemas used as a source. schemas: description: Avro 1.9 - storage: Git repository where source code is. + storage: Git repository where source code is. During release they are published to Confluent Schema registry. registry: Confluent Schema registry versioning: TODO: how is it done? validation: TODO: how is validation done in runtime From ccc218355a63c9f98046ae408fa86ec4031c6223 Mon Sep 17 00:00:00 2001 From: derberg Date: Wed, 7 Sep 2022 18:35:40 +0200 Subject: [PATCH 06/28] added more details on versioning, validation and correlationId --- public/casestudies/adeo.yml | 126 +++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 10 deletions(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 1a5b94291daa..472fef45b7c9 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -36,9 +36,10 @@ technical: Message Header object with `REPLY_TOPIC` header property. Info that needs to be provided by client, so producer knows where to send a response. TODO: Can we have a snippet? - [Correlation identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) - Important to follow to be able to identify what request does the respons match. - `REQUEST_ID` in Message Header object, and pointed out with `correlationID` property. - TODO: I could not figure out what role `CORRELATION_ID` is playing. Is it that once producer sends a message to reply channel, then info about request id is not in `REQUEST_ID` but `CORRELATION_ID`. So they both have the same info, but one is for request producer and the other for reply producer? + This pattern enables identification of request given response was sent to. + + `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. + This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. TODO: Can we have a snippet? - [DeadLetter channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. In AsyncAPI for now it is just part of channel description, channel that works as DeadLetter channel. @@ -47,28 +48,133 @@ codegen: | Java models generation. Avro schemas used as a source. schemas: description: Avro 1.9 - storage: Git repository where source code is. During release they are published to Confluent Schema registry. - registry: Confluent Schema registry - versioning: TODO: how is it done? - validation: TODO: how is validation done in runtime + storage: Git repository where source code is. During release they are published to Confluent Schema Registry. + registry: Confluent Schema Registry. + versioning: Versioning is based on git tags. Schema version pushed to Confluent Schema Registry match the git tag version of the product. Every schema has a `version` information, that match with product tag version. + validation: Based on validation on a broker level using Confluent Schema Registry. asyncapi: usecase: | Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation for machine-readable document that describes the API. The goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company. versions: - - 2.0.0 TODO: + - 2.4.0 storage: Git repository where source code is. maintainers: Developers - editing: TODO: please provide info + editing: IntelliJ without any special plugins audience: internal: true external: false - extensions: TODO: do you use any? + extensions: null documentation: | Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. Publishing is part of CI/CD pipeline for the product using GitLabCI. + Related Maven configuration used to trigger docs generation with AsyncAPI Generator industry: + + ``` + + generate-asyncapi-doc + + + + com.github.eirslett + frontend-maven-plugin + + ${frontend-maven-plugin.version} + + v12.18.4 + ${node.installation.path} + ${node.installation.path} + + + + install node and npm + + install-node-and-npm + + generate-resources + + + install @asyncapi/generator globally + + npm + + + install @asyncapi/generator@${asyncapi.generator.version} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + + execute-generation + + exec + + generate-resources + + + + ${node.modules.installation.path}/${ag.binary.name} + + ${project.basedir}/src/docs/asyncapi/asyncapi.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p + version=${project.version} -o ${asyncapi.generation.dir} + + + + + execute-internal-generation + + exec + + generate-resources + + + + ${node.modules.installation.path}/${ag.binary.name} + + ${project.basedir}/src/docs/asyncapi/asyncapi-internal.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p + version=${project.version} -o ${asyncapi.generation.dir}/internal + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + + generate-resources + + copy-resources + + + ${asyncapi.generation.dir}/assets + + + src/docs/asyncapi/assets + true + + + + + + + + + + ``` + Critical features of AsyncAPI related to documentation: - `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs - examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated From a5a034eeb521a8396eb608757859da5c14ee3c88 Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 8 Sep 2022 17:18:39 +0200 Subject: [PATCH 07/28] add examples :rocket: --- casestudy_template.yml | 4 +- public/casestudies/adeo.yml | 117 ++++++- public/img/casestudies/adeo/architecture.webp | Bin 0 -> 52664 bytes .../adeo/CostingRequestPayload.avsc | 126 ++++++++ .../casestudies/adeo/CostingResponseKey.avsc | 11 + .../adeo/CostingResponsePayload.avsc | 79 +++++ .../resources/casestudies/adeo/asyncapi.yaml | 303 ++++++++++++++++++ 7 files changed, 622 insertions(+), 18 deletions(-) create mode 100644 public/img/casestudies/adeo/architecture.webp create mode 100644 public/resources/casestudies/adeo/CostingRequestPayload.avsc create mode 100644 public/resources/casestudies/adeo/CostingResponseKey.avsc create mode 100644 public/resources/casestudies/adeo/CostingResponsePayload.avsc create mode 100644 public/resources/casestudies/adeo/asyncapi.yaml diff --git a/casestudy_template.yml b/casestudy_template.yml index 30c6d865af8f..faf0c098f998 100644 --- a/casestudy_template.yml +++ b/casestudy_template.yml @@ -60,9 +60,9 @@ asyncapi: parser: plugins: - avro-schema-parser + fullExample: #should be url to full example of the case study. So later in the UI we can show it as "https://studio.asyncapi.com/?url=https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml" so the example can be previewed in the AsyncAPI Studio easily. contact: | #Let us know who we can contact you, some generic email or just a name and link to some social media. We need this to be able tools: - validate long term if case study is still validate - ping you when we know we plan to change some parts of AsyncAPI that you are already using -example: | - #Provide full example of AsyncAPI file that you are using on production. \ No newline at end of file +additionalResources: #provide some additional resources if there are such. Just block of text with links to articles or videos about the case study. \ No newline at end of file diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 472fef45b7c9..30fb5dc4cfb4 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -25,25 +25,64 @@ technical: - Spring protocols: - Kafka - brokers: TODO: size of cluster - testing: TODO: how is it done + brokers: Kafka with Kafka Connect component. There are 15 production brokers with 47 topics. + testing: For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. architecture: | The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: - - [Request/reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) + - [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel. - TODO: Can we have a snippet? - - [Return address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) - Message Header object with `REPLY_TOPIC` header property. Info that needs to be provided by client, so producer knows where to send a response. - TODO: Can we have a snippet? - - [Correlation identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) + + Example description of response channel: + ``` + description: > + This topic is used to REPLY Costing Requests and is targeted by the + `REPLY_TOPIC` header. + ``` + - [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) + Info that needs to be provided by client, so producer knows where to send a response. Information is sent in message header with `REPLY_TOPIC` property. In the AsyncAPI file, information is documented as part of Message Header object. + + Example of request message header with `REPLY_TOPIC`: + ``` + headers: + type: object + required: + - REPLY_TOPIC + properties: + REPLY_TOPIC: + $ref: "#/components/schemas/ReplyTopic" + ``` + - [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) This pattern enables identification of request given response was sent to. `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. - TODO: Can we have a snippet? - - [DeadLetter channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) - Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. In AsyncAPI for now it is just part of channel description, channel that works as DeadLetter channel. - TODO: Can we have a snippet? + + Example of request message header with `REQUEST_ID`: + ``` + headers: + type: object + required: + - REQUEST_ID + properties: + REQUEST_ID: + $ref: "#/components/schemas/RequestId" + ``` + + Example of how `correlationId` points to `REQUEST_ID`: + ``` + description: > + This correlation ID is used for message tracing and messages + correlation. + This correlation ID is generated at runtime based on the `REQUEST_ID` + and sent to the RESPONSE message. + location: $message.header#/REQUEST_ID + ```` + - [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) + Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. + - [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html) + Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. + + ![Architecture Diagram](/img/casestudies/adeo/architecture.webp) codegen: | Java models generation. Avro schemas used as a source. schemas: @@ -61,7 +100,7 @@ asyncapi: storage: Git repository where source code is. maintainers: Developers - editing: IntelliJ without any special plugins + editing: IntelliJ without any special plugins. Sometimes people use AsyncAPI Studio, but not regularly because of lack of support for references to local drive. audience: internal: true external: false @@ -181,8 +220,53 @@ asyncapi: - `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI bindings: kafka: | - Kafka bindings are used on channel, operation and message level. - TODO: but channel bindings that you use in your examples are not in https://github.com/asyncapi/bindings/tree/master/kafka#channel-binding-object + All Kafka bindings are used. Server, channel, operation and message bindings. + + Example of server bindings: + ``` + bindings: + kafka: + schema.registry.url: >- + https://schema-registry.prod.url/ + ``` + + Example of channel bindings: + ``` + bindings: + kafka: + replicas: 3 + partitions: 3 + cleanup.policy: delete + retention.ms: 7 days + ``` + + Example of operation bindings: + ``` + bindings: + kafka: + groupId: + type: string + description: > + The groupId must be prefixed by your `svc` account, deliver by the + Adeo Kafka team. + This `svc` must have the write access to the topic. + value.subject.name.strategy: + type: string + description: > + We use the RecordNameStrategy to infer the messages schema. + Use + `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy` + in your producer configuration. + ``` + + Example of message bindings: + ``` + bindings: + kafka: + key: + $ref: "https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc" + ``` + tools: generator: templates: @@ -193,6 +277,7 @@ asyncapi: parser: plugins: - avro-schema-parser + fullExample: https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml contact: | TODO: -example: TODO: please provide one full example \ No newline at end of file +additionalResources: Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. diff --git a/public/img/casestudies/adeo/architecture.webp b/public/img/casestudies/adeo/architecture.webp new file mode 100644 index 0000000000000000000000000000000000000000..5dec80035a514d7e726e687fe14840ad68111490 GIT binary patch literal 52664 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 F{9i+dV9Wpj literal 0 HcmV?d00001 diff --git a/public/resources/casestudies/adeo/CostingRequestPayload.avsc b/public/resources/casestudies/adeo/CostingRequestPayload.avsc new file mode 100644 index 000000000000..612337b907a3 --- /dev/null +++ b/public/resources/casestudies/adeo/CostingRequestPayload.avsc @@ -0,0 +1,126 @@ +{ + "namespace": "com.adeo.casestudy.costingrequest", + "type": "record", + "name": "CostingRequestPayload", + "version": "1.1.0", + "fields": [ + { + "name": "ProductId", + "type": { + "type": "string", + "avro.java.string": "String" + }, + "doc": "Product identifier." + }, + { + "name": "ShortProductDescription", + "type": [ + "null", + { + "type": "string", + "avro.java.string": "String" + } + ], + "doc": "A short product description.", + "example": "BOTTLE RACK, STACKABLE, 2 TIER, 10 BOTTLES" + }, + { + "name": "SupplierCode", + "type": { + "type": "string", + "avro.java.string": "String" + }, + "doc": "Supplier code, used to get parameter values that depend on the supplier.", + "example": "12345678" + }, + { + "name": "SupplierPrice", + "type": "float", + "doc": "Product price (purchasing price)." + }, + { + "name": "Unit", + "doc": "Unit characteristics", + "type": { + "name": "UnitItem", + "namespace": "com.adeo.casestudy.costingrequest", + "type": "record", + "fields": [ + { + "name": "Length", + "type": "float", + "example": 1.11, + "exclusiveMinimum": 0 + }, + { + "name": "Width", + "type": "float", + "example": 1.11, + "exclusiveMinimum": 0 + }, + { + "name": "Height", + "type": "float", + "example": 1.11, + "exclusiveMinimum": 0 + }, + { + "name": "WeightGross", + "type": "float", + "example": 6.68, + "exclusiveMinimum": 0 + }, + { + "name": "WeightNet", + "type": "float", + "example": 6.68, + "exclusiveMinimum": 0, + "doc": "The unit of net weight is the kilogram" + } + ] + } + }, + { + "name": "BusInputs", + "type": { + "type": "array", + "doc": "Business units specific data required to deduce the right BU context.", + "minItems": 1, + "items": { + "name": "CostingRequestBUItem", + "namespace": "com.adeo.casestudy.costingrequest", + "type": "record", + "fields": [ + { + "name": "ClientCode", + "type": { + "type": "string", + "avro.java.string": "String" + }, + "doc": "The client (Business Unit) code.", + "example": "1" + }, + { + "name": "ContainerType", + "type": [ + "null", + { + "type": "enum", + "namespace": "com.adeo.casestudy.model", + "name": "ContainerTypeItems", + "doc": "Container selected for transportation. Required to determinate parameter values that rely on transportation. **Will be deleted (because deduced) in future versions**", + "symbols": [ + "FT20", + "FT40", + "FT40HC", + "TRUCK" + ] + } + ] + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/public/resources/casestudies/adeo/CostingResponseKey.avsc b/public/resources/casestudies/adeo/CostingResponseKey.avsc new file mode 100644 index 000000000000..d1b6c628b0c6 --- /dev/null +++ b/public/resources/casestudies/adeo/CostingResponseKey.avsc @@ -0,0 +1,11 @@ +{ + "namespace": "com.adeo.casestudy.costingresponse", + "type": "record", + "name": "CostingResponseKey", + "version": 1, + "fields": [ + {"name": "RequesterCode", "type": { "type": "string", "avro.java.string": "String" }, "doc": "Requester code"}, + {"name": "ClientCode", "type": { "type": "string", "avro.java.string": "String" }, "doc": "The client (Business Unit) code the response is for.", "example": "1"}, + {"name": "ProductId", "type": { "type": "string", "avro.java.string": "String" }, "doc": "Product Id.", "example": "12345678"} + ] +} diff --git a/public/resources/casestudies/adeo/CostingResponsePayload.avsc b/public/resources/casestudies/adeo/CostingResponsePayload.avsc new file mode 100644 index 000000000000..64ea47b2576b --- /dev/null +++ b/public/resources/casestudies/adeo/CostingResponsePayload.avsc @@ -0,0 +1,79 @@ +{ + "namespace": "com.adeo.casestudy.costingresponse", + "type": "record", + "name": "CostingResponsePayload", + "version": 1, + "fields": [ + {"name": "CostingResult", "type": ["null", + { + "name": "CostingResultItem", + "namespace": "com.adeo.casestudy.costingresponse", + "type": "record", + "doc": "Costing result data", + "fields" : [ + {"name": "CalculationPrice", "type": "float", "doc": "The calculated price.", "example": 1.136}, + {"name": "CalculationCurrency", "doc": "The currency for the calculation price.", "type": + { + "name": "CalculationCurrencyItems", + "namespace": "com.adeo.casestudy.costingresponse", + "type": "enum", + "symbols": ["USD", "EUR", "CNY"], + "default": "EUR" + } + }, + {"name": "CalculationDetails", "type": { + "name": "CostingResultDetailsItems", + "namespace": "com.adeo.casestudy.costingresponse", + "type": "array", + "items": + { + "name": "CostingResultDetailsItem", + "namespace": "com.adeo.casestudy.costingresponse", + "type": "record", + "doc": "Cost Component used during the calculation", + "fields": [ + {"name": "Code", "type": {"type": "string", "avro.java.string": "String"}, "doc": "The Cost Component Code.", "example": "CODE1"}, + {"name": "Formula", "type": {"type": "string", "avro.java.string": "String"}, "doc": "The Cost Component Formula.", "example": "PARAM1 / PARAM2"}, + {"name": "Value", "type": "float", "doc": "Cost component calculated value.", "example": 1.145} + ] + } + } + } + ] + }] + }, + {"name": "CostingErrors", "doc": "Technical or functional errors occurred during calculation", "type": ["null", { + "type": "array", + "name": "CostingErrorItems", + "namespace": "com.adeo.casestudy.costingresponse", + "items": + { + "name": "CostingErrorItem", + "namespace": "com.adeo.casestudy.costingresponse", + "type": "record", + "doc": "Costing errors data", + "fields": [ + {"name": "Type", "type": + { + "name": "CostingErrorTypeItems", + "namespace": "com.adeo.casestudy.costingresponse", + "type": "enum", + "symbols": ["TECHNICAL", "FUNCTIONAL"], + "default": "FUNCTIONAL" + } + }, + { + "name" : "Code", + "type" : { + "type" : "string", + "avro.java.string" : "String" + }, + "doc" : "Error codification" + }, + {"name": "Step", "type": {"type": "string", "avro.java.string": "String"}, "doc": "Costing step where the error occurred"}, + {"name": "Description", "type": {"type": "string", "avro.java.string": "String"}, "doc": "Error description"} + ]} + }] + } + ] +} diff --git a/public/resources/casestudies/adeo/asyncapi.yaml b/public/resources/casestudies/adeo/asyncapi.yaml new file mode 100644 index 000000000000..73c0d899c0d4 --- /dev/null +++ b/public/resources/casestudies/adeo/asyncapi.yaml @@ -0,0 +1,303 @@ +asyncapi: 2.4.0 +info: + title: Adeo Async API Case Study + version: "%REPLACED_BY_MAVEN%" + description: > + This Adeo specification illustrates how ADEO uses Async API to document some of their exchanges + contact: + name: AsyncApi team + email: case-study@asyncapi.com +servers: + production: + url: "prod.url:9092" + protocol: kafka + description: Kafka PRODUCTION cluster + security: + - sasl-ssl: [] + bindings: + kafka: + schema.registry.url: >- + https://schema-registry.prod.url/ + staging: + url: "staging.url:9092" + protocol: kafka + description: Kafka STAGING cluster for `uat` and `preprod` environments + security: + - sasl-ssl: [] + bindings: + kafka: + schema.registry.url: >- + https://schema-registry.staging.url/ + dev: + url: "dev.url:9092" + protocol: kafka + description: Kafka DEV cluster for `dev` and `sit` environments + security: + - sasl-ssl: [] + bindings: + kafka: + schema.registry.url: >- + https://schema-registry.dev.url/ +tags: + - name: costing + description: "Costing channels, used by Costing clients." +channels: + "adeo-{env}-case-study-COSTING-REQUEST-{version}": + description: > + Use this topic to do a Costing Request to Costing product. + We use the + [**RecordNameStrategy**](https://docs.confluent.io/platform/current/schema-registry/serdes-develop/index.html#subject-name-strategy) + to infer the messages schema. + You have to define `value.subject.name.strategy` to + `io.confluent.kafka.serializers.subject.RecordNameStrategy` in your + producer to use the schema we manage. + The schema below illustrates how Costing Request messages are + handled. + ![](https://user-images.githubusercontent.com/5501911/188920831-689cec5f-8dc3-460b-8794-0b54ec8b0ac8.png) + parameters: + env: + $ref: "#/components/parameters/Env" + version: + $ref: "#/components/parameters/Version" + bindings: + kafka: + replicas: 3 + partitions: 3 + cleanup.policy: delete + retention.ms: 7 days + publish: + operationId: requestCosting + summary: | + [COSTING] Request one or more Costing calculation for any product + description: > + You can try a costing request using our [Conduktor producer + template](https://conduktor.url) + tags: + - name: costing + message: + oneOf: + - $ref: "#/components/messages/costingRequestV1" + bindings: + kafka: + groupId: + type: string + description: > + The groupId must be prefixed by your `svc` account, deliver by the + Adeo Kafka team. + This `svc` must have the write access to the topic. + value.subject.name.strategy: + type: string + description: > + We use the RecordNameStrategy to infer the messages schema. + Use + `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy` + in your producer configuration. + "adeo-{env}-case-study-COSTING-RESPONSE-{version}": + description: > + This topic is used to REPLY Costing Requests and is targeted by the + `REPLY_TOPIC` header. + **You must grant PUBLISH access to our `svc-ccr-app` service account.**. + We use the + [**RecordNameStrategy**](https://docs.confluent.io/platform/current/schema-registry/serdes-develop/index.html#subject-name-strategy) + to infer the messages schema. + You have to define `key.subject.name.strategy` and + `value.subject.name.strategy` to + `io.confluent.kafka.serializers.subject.RecordNameStrategy` in your + consumer. + The schema below illustrates how Costing Response messages are + handled. + ![](https://user-images.githubusercontent.com/5501911/188920831-689cec5f-8dc3-460b-8794-0b54ec8b0ac8.png) + parameters: + env: + $ref: "#/components/parameters/Env" + version: + $ref: "#/components/parameters/Version" + bindings: + kafka: + groupId: + type: string + description: > + The groupId must be prefixed by your `svc` account, deliver by the + Adeo Kafka team. + This `svc` must have the read access to the topic. + key.subject.name.strategy: + type: string + description: > + We use the RecordNameStrategy to infer the messages schema. + Use + `key.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy` + in your consumer configuration. + value.subject.name.strategy: + type: string + description: > + We use the RecordNameStrategy to infer the messages schema. + Use + `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy` + in your consumer configuration. + subscribe: + operationId: getCostingResponse + summary: > + [COSTING] Get the costing responses matching an initial Costing + Request. + tags: + - name: costing + message: + $ref: "#/components/messages/costingResponse" +components: + correlationIds: + costingCorrelationId: + description: > + This correlation ID is used for message tracing and messages + correlation. + This correlation ID is generated at runtime based on the `REQUEST_ID` + and sent to the RESPONSE message. + location: $message.header#/REQUEST_ID + messages: + costingRequestV1: + name: CostingRequestV1 + title: Costing Request V1 + summary: Costing Request V1 inputs. + tags: + - name: costing + schemaFormat: application/vnd.apache.avro;version=1.9.0 + correlationId: + $ref: "#/components/correlationIds/costingCorrelationId" + headers: + type: object + required: + - REQUESTER_ID + - REQUESTER_CODE + - REQUEST_ID + - REPLY_TOPIC + properties: + REQUEST_ID: + $ref: "#/components/schemas/RequestId" + REPLY_TOPIC: + $ref: "#/components/schemas/ReplyTopic" + REQUESTER_ID: + $ref: "#/components/schemas/RequesterId" + REQUESTER_CODE: + $ref: "#/components/schemas/RequesterCode" + payload: + $ref: "https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingRequestPayload.avsc" + costingResponse: + name: CostingResponse + title: Costing Response + summary: Costing Response ouputs. + tags: + - name: costing + description: > + Please refer to the `CostingResponseKey.avsc` schema, available on [our + github + project](https://github.url/). + schemaFormat: application/vnd.apache.avro;version=1.9.0 + correlationId: + $ref: "#/components/correlationIds/costingCorrelationId" + headers: + type: object + properties: + CALCULATION_ID: + $ref: "#/components/schemas/MessageId" + CORRELATION_ID: + $ref: "#/components/schemas/CorrelationId" + REQUEST_TIMESTAMP: + type: string + format: date-time + description: Timestamp of the costing request + CALCULATION_TIMESTAMP: + type: string + format: date-time + description: Technical timestamp for the costing calculation + bindings: + kafka: + key: + $ref: "https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc" + payload: + $ref: "https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponsePayload.avsc" + schemas: + RequesterId: + type: string + description: The Costing requester service account used to produce costing request. + example: svc-ecollect-app + RequesterCode: + type: string + description: >- + The Costing requester code (generally the BU Code). The requester code + is useful to get the dedicated context (tenant). + example: 1 + MessageId: + type: string + format: uuid + description: A unique Message ID. + example: 1fa6ef40-8f47-40a8-8cf6-f8607d0066ef + RequestId: + type: string + format: uuid + description: >- + A unique Request ID needed to define a `CORRELATION_ID` for exchanges, + which will be sent back in the Costing Responses. + example: 1fa6ef40-8f47-40a8-8cf6-f8607d0066ef + CorrelationId: + type: string + format: uuid + description: >- + A unique Correlation ID defined from the `REQUEST_ID` or the + `MESSAGE_ID` provided in the Costing Request. + example: 1fa6ef40-8f47-40a8-8cf6-f8607d0066ef + BuCode: + type: string + description: The Business Unit code for which data are applicable. + example: 1 + ReplyTopic: + type: string + description: > + The Kafka topic where to send the Costing Response. This is required for + the [Return Address EIP + pattern](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html). + **You must grant WRITE access to our `svc-ccr-app` service account.** + example: adeo-case-study-COSTING-RESPONSE-V1 + ErrorStep: + type: string + description: | + The woker that has thrown the error. + example: EXPOSE_RESULT + ErrorMessage: + type: string + description: | + The error message describing the error. + example: Error message + ErrorCode: + type: string + description: | + The error code. + example: CURRENCY_NOT_FOUND + parameters: + Env: + description: Adeo Kafka Environement for messages publications. + schema: + type: string + enum: + - dev + - sit + - uat1 + - preprod + - prod + Version: + description: the topic version you want to use + schema: + type: string + example: V1 + default: V1 + securitySchemes: + sasl-ssl: + type: plain + x-sasl.jaas.config: >- + org.apache.kafka.common.security.plain.PlainLoginModule required + username="" password=""; + x-security.protocol: SASL_SSL + x-ssl.endpoint.identification.algorithm: https + x-sasl.mechanism: PLAIN + description: > + Use [SASL authentication with SSL + encryption](https://docs.confluent.io/platform/current/security/security_tutorial.html#configure-clients) + to connect to the ADEO Broker. \ No newline at end of file From 7b06881d7d35d3ae55450dcc5a9dcea0dade0a7c Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 8 Sep 2022 17:30:04 +0200 Subject: [PATCH 08/28] Update netlify.toml --- netlify.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/netlify.toml b/netlify.toml index 1c7c5a8883d6..3f69955de753 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,6 +2,7 @@ for = "/*" [headers.values] X-Frame-Options = "ALLOW-FROM https://www.youtube.com/" + Access-Control-Allow-Origin = "*" [build] command = "npm run build && npm run export" From 86bfbbbce83e8a192319ccb798dfa5b2e662e0b4 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Thu, 8 Sep 2022 20:14:06 +0200 Subject: [PATCH 09/28] Apply suggestions from code review Co-authored-by: Ludovic Dussart --- public/casestudies/adeo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 30fb5dc4cfb4..1da03f87a113 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -7,7 +7,7 @@ company: website: https://www.adeo.com/ logo: https://www.adeo.com/assets-adeo/themes/adeo-refonte/dist/assets/images/logo-adeo-v3.svg challenges: | - Cost Component Repository product is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use differen information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. + Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. Initial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel. @@ -107,7 +107,7 @@ asyncapi: extensions: null documentation: | Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. - Publishing is part of CI/CD pipeline for the product using GitLabCI. + Publishing is part of CI/CD pipeline for the product using GithubActions. Related Maven configuration used to trigger docs generation with AsyncAPI Generator industry: From 6102241f833b53a5dd7c755dde25c4b6600a5baa Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 8 Sep 2022 20:14:51 +0200 Subject: [PATCH 10/28] add contact --- public/casestudies/adeo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 30fb5dc4cfb4..d2801d79745f 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -266,7 +266,7 @@ asyncapi: key: $ref: "https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc" ``` - + tools: generator: templates: @@ -279,5 +279,5 @@ asyncapi: - avro-schema-parser fullExample: https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml contact: | - TODO: + - [Ludovic Dussart](https://twitter.com/ldussart) additionalResources: Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. From afadc1571a33eec7aa568343879a74b56419e54a Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 22 Sep 2022 08:55:58 +0200 Subject: [PATCH 11/28] fix spelling mistake --- public/casestudies/adeo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 8b200d27e2df..9989fa95dd22 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -93,7 +93,7 @@ schemas: validation: Based on validation on a broker level using Confluent Schema Registry. asyncapi: usecase: | - Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation for machine-readable document that describes the API. + Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API. The goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company. versions: - 2.4.0 From 6c0e075336011c9e294d1c77b256fef153364f75 Mon Sep 17 00:00:00 2001 From: derberg Date: Tue, 27 Sep 2022 11:51:05 +0200 Subject: [PATCH 12/28] update maven config --- public/casestudies/adeo.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index 9989fa95dd22..ea73674d6fd3 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -168,22 +168,6 @@ asyncapi: - - execute-internal-generation - - exec - - generate-resources - - - - ${node.modules.installation.path}/${ag.binary.name} - - ${project.basedir}/src/docs/asyncapi/asyncapi-internal.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p - version=${project.version} -o ${asyncapi.generation.dir}/internal - - - From f28747f2a78f80d8387ff8ccd682602379252ff2 Mon Sep 17 00:00:00 2001 From: derberg Date: Tue, 4 Oct 2022 17:22:30 +0200 Subject: [PATCH 13/28] improve structure of info basing on discussion with Maya that works on design --- casestudy_template.yml | 14 ++++++++------ public/casestudies/adeo.yml | 11 ++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/casestudy_template.yml b/casestudy_template.yml index faf0c098f998..e4c4f34f502a 100644 --- a/casestudy_template.yml +++ b/casestudy_template.yml @@ -6,6 +6,12 @@ company: #This is optional, if company wants to provide case study data anonymou revenue: #Specify company earnings to indicate to case study reader how critical online operations are. website: #Link to main website. logo: #Link to logo of the company in SVG format. + contact: + #Let us know who we can contact you, some generic email or just a name and link to some social media. We need this to be able tools: + #- validate long term if case study is still validate + #- ping you when we know we plan to change some parts of AsyncAPI that you are already using + - name: #full name of the contact person + twitter: #specify if twitter, linkedin, email, so we can nicely render challenges: | #Describe want challanges company have, that faced them towards AsyncAPI. There is no limit here. Use [CommonMark](https://commonmark.org/). solution: | @@ -23,8 +29,8 @@ technical: #Explain how testing of the solution is done. How you ensure API works as expected after changes, that users are not affected. For example how do you know who will be affected if you stop sending some message? Provide examples if possible. architecture: | #Explain the setup of the EDA architecture. Perfect woudl be if you could point what [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) you applied. Provide examples if possible. -codegen: | - #Explain if you use code generation, what you generate and how. Provide examples if possible. + codegen: | + #Explain if you use code generation, what you generate and how. Provide examples if possible. schemas: description: #What spec and version do you use, is it JSON Schema? Avro? What version? storage: #Where do you store your schemas @@ -61,8 +67,4 @@ asyncapi: plugins: - avro-schema-parser fullExample: #should be url to full example of the case study. So later in the UI we can show it as "https://studio.asyncapi.com/?url=https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml" so the example can be previewed in the AsyncAPI Studio easily. -contact: | - #Let us know who we can contact you, some generic email or just a name and link to some social media. We need this to be able tools: - - validate long term if case study is still validate - - ping you when we know we plan to change some parts of AsyncAPI that you are already using additionalResources: #provide some additional resources if there are such. Just block of text with links to articles or videos about the case study. \ No newline at end of file diff --git a/public/casestudies/adeo.yml b/public/casestudies/adeo.yml index ea73674d6fd3..366d416a4bb6 100644 --- a/public/casestudies/adeo.yml +++ b/public/casestudies/adeo.yml @@ -6,6 +6,9 @@ company: revenue: 25.6B EURO turnover, including 768M EURO online website: https://www.adeo.com/ logo: https://www.adeo.com/assets-adeo/themes/adeo-refonte/dist/assets/images/logo-adeo-v3.svg + contact: + - name: Ludovic Dussart + twitter: ldussart challenges: | Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. @@ -83,8 +86,8 @@ technical: Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. ![Architecture Diagram](/img/casestudies/adeo/architecture.webp) -codegen: | - Java models generation. Avro schemas used as a source. + codegen: | + Java models generation. Avro schemas used as a source. schemas: description: Avro 1.9 storage: Git repository where source code is. During release they are published to Confluent Schema Registry. @@ -262,6 +265,4 @@ asyncapi: plugins: - avro-schema-parser fullExample: https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml -contact: | - - [Ludovic Dussart](https://twitter.com/ldussart) -additionalResources: Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. +additionalResources: Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. \ No newline at end of file From 6f4c08c31e154a9ffed615b141df734c43483984 Mon Sep 17 00:00:00 2001 From: derberg Date: Sat, 24 Dec 2022 15:20:52 +0100 Subject: [PATCH 14/28] adding simplistic UI --- components/CaseStudyCard.js | 31 + components/MDX.js | 4 +- config/case-studies.json | 1 + {public => config}/casestudies/adeo.yml | 41 +- package-lock.json | 862 ++++++++++++++++++------ package.json | 4 +- pages/_app.js | 2 +- pages/casestudies/[id].js | 298 ++++++++ pages/casestudies/index.js | 49 ++ public/img/casestudies/adeo/logo.svg | 65 ++ scripts/casestudies/index.js | 28 + scripts/tools/tools-object.js | 15 +- scripts/utils.js | 16 + 13 files changed, 1166 insertions(+), 250 deletions(-) create mode 100644 components/CaseStudyCard.js create mode 100644 config/case-studies.json rename {public => config}/casestudies/adeo.yml (91%) create mode 100644 pages/casestudies/[id].js create mode 100644 pages/casestudies/index.js create mode 100644 public/img/casestudies/adeo/logo.svg create mode 100644 scripts/casestudies/index.js create mode 100644 scripts/utils.js diff --git a/components/CaseStudyCard.js b/components/CaseStudyCard.js new file mode 100644 index 000000000000..8231bed97158 --- /dev/null +++ b/components/CaseStudyCard.js @@ -0,0 +1,31 @@ +import Paragraph from './typography/Paragraph'; + +export default function CaseStudyCard({ + studies = [] +}) { + if(studies.length === 0){ + return null; + } + return ( +
+ {studies.map((study, index) => ( + +
+ + + {study.company.name} + + + + { study.company.description } + +
+ + ))} +
+ ); +} diff --git a/components/MDX.js b/components/MDX.js index daa7ed720524..0b6db871d6da 100644 --- a/components/MDX.js +++ b/components/MDX.js @@ -25,7 +25,7 @@ let mermaidInitialized = false; initializeMermaid(); const mdxComponents = getMDXComponents(); -export default function MDXProvider({ children }) { +export function MDXProvider({ children }) { return ( {children} @@ -33,7 +33,7 @@ export default function MDXProvider({ children }) { ); } -function getMDXComponents() { +export function getMDXComponents() { return { h1: props =>

, h2: props =>

, diff --git a/config/case-studies.json b/config/case-studies.json new file mode 100644 index 000000000000..fb646bbcfd6b --- /dev/null +++ b/config/case-studies.json @@ -0,0 +1 @@ +[{"id":"adeogroup","company":{"name":"Adeo Group","description":"Adeo owns different brands in retail industry focused on home improvement and DIY markets, like Leroy Merlin.","customers":"500M","industry":"Retail","revenue":"25.6B EURO turnover, including 768M EURO online.","website":"https://www.adeo.com/","logo":"/img/casestudies/adeo/logo.svg","contact":[{"name":"Ludovic Dussart","link":"https://twitter.com/ldussart"}]},"challenges":"Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy.\n\nInitial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel.\n\nThere was a need for a standart way of describing event-driven architecture.\n","solution":"The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API.\n\nPayloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs.\n\nShift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files.\n","technical":{"languages":["Java"],"frameworks":["Spring"],"protocols":["Kafka"],"brokers":"Kafka with Kafka Connect component. There are 15 production brokers with 47 topics.","testing":"For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro.","architecture":"The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied:\n- [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html)\n Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel.\n \n Example description of response channel:\n ```\n description: >\n This topic is used to REPLY Costing Requests and is targeted by the\n `REPLY_TOPIC` header.\n ```\n- [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html)\n Info that needs to be provided by client, so producer knows where to send a response. Information is sent in message header with `REPLY_TOPIC` property. In the AsyncAPI file, information is documented as part of Message Header object.\n \n Example of request message header with `REPLY_TOPIC`:\n ```\n headers:\n type: object\n required:\n - REPLY_TOPIC\n properties:\n REPLY_TOPIC:\n $ref: \"#/components/schemas/ReplyTopic\"\n ```\n- [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html)\n This pattern enables identification of request given response was sent to. \n \n `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property.\n This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply.\n \n Example of request message header with `REQUEST_ID`:\n ```\n headers:\n type: object\n required:\n - REQUEST_ID\n properties:\n REQUEST_ID:\n $ref: \"#/components/schemas/RequestId\"\n ```\n\n Example of how `correlationId` points to `REQUEST_ID`:\n ```\n description: >\n This correlation ID is used for message tracing and messages\n correlation.\n This correlation ID is generated at runtime based on the `REQUEST_ID`\n and sent to the RESPONSE message.\n location: $message.header#/REQUEST_ID\n ````\n- [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html)\n Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events.\n- [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html)\n Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events.\n\n![Architecture Diagram](/img/casestudies/adeo/architecture.webp)\n","codegen":"Java models generation. Avro schemas used as a source.\n"},"schemas":{"description":"Avro 1.9","storage":"Git repository where source code is. During release they are published to Confluent Schema Registry.","registry":"Confluent Schema Registry.","versioning":"Versioning is based on git tags. Schema version pushed to Confluent Schema Registry match the git tag version of the product. Every schema has a `version` information, that match with product tag version.","validation":"Based on validation using Confluent Schema Registry."},"asyncapi":{"usecase":"Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API.\nThe goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company.\n","versions":["2.4.0"],"storage":"Git repository where source code is.","maintainers":"Developers","editing":"IntelliJ without any special plugins. Sometimes people use AsyncAPI Studio, but not regularly because of lack of support for references to local drive.","audience":{"internal":true,"external":false},"extensions":"Extensions are used to describe details about custom security:\n```yml\n x-sasl.jaas.config: >-\n org.apache.kafka.common.security.plain.PlainLoginModule required\n username=\"\" password=\"\";\n x-security.protocol: SASL_SSL\n x-ssl.endpoint.identification.algorithm: https\n x-sasl.mechanism: PLAIN\n````\n","documentation":"Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. \nPublishing is part of CI/CD pipeline for the product using GithubActions.\n\nRelated Maven configuration used to trigger docs generation with AsyncAPI Generator industry:\n\n```\n \n generate-asyncapi-doc\n \n \n \n com.github.eirslett\n frontend-maven-plugin\n \n ${frontend-maven-plugin.version}\n \n v12.18.4\n ${node.installation.path}\n ${node.installation.path}\n \n \n \n install node and npm\n \n install-node-and-npm\n \n generate-resources\n \n \n install @asyncapi/generator globally\n \n npm\n \n \n install @asyncapi/generator@${asyncapi.generator.version}\n \n \n \n \n \n org.codehaus.mojo\n exec-maven-plugin\n 1.6.0\n\n \n \n execute-generation\n \n exec\n \n generate-resources\n\n \n \n ${node.modules.installation.path}/${ag.binary.name}\n \n ${project.basedir}/src/docs/asyncapi/asyncapi.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p\n version=${project.version} -o ${asyncapi.generation.dir}\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-resources-plugin\n \n \n copy-resources\n \n generate-resources\n \n copy-resources\n \n \n ${asyncapi.generation.dir}/assets\n \n \n src/docs/asyncapi/assets\n true\n \n \n \n \n \n \n \n \n \n```\n\nCritical features of AsyncAPI related to documentation:\n- `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs\n- examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated\n- `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI\n","bindings":{"kafka":"All Kafka bindings are used. Server, channel, operation and message bindings.\n\nExample of server bindings:\n```\nbindings:\n kafka:\n schema.registry.url: >-\n https://schema-registry.prod.url/\n```\n\nExample of channel bindings:\n```\nbindings:\n kafka:\n replicas: 3\n partitions: 3\n cleanup.policy: delete\n retention.ms: 7 days\n```\n\nExample of operation bindings:\n```\nbindings:\n kafka:\n groupId:\n type: string\n description: >\n The groupId must be prefixed by your `svc` account, deliver by the\n Adeo Kafka team.\n This `svc` must have the write access to the topic.\n value.subject.name.strategy:\n type: string\n description: >\n We use the RecordNameStrategy to infer the messages schema.\n Use\n `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy`\n in your producer configuration.\n```\n\nExample of message bindings:\n```\nbindings:\n kafka:\n key:\n $ref: \"https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc\"\n```\n"},"tools":"* [AsyncAPI Generator](https://github.com/asyncapi/generator):\n * [HTML Template](https://github.com/asyncapi/html-template) with parameters like `sidebarOrganization=byTags` and `version`.\n* [AsyncAPI JavaScript Parser](https://github.com/asyncapi/parser-js) with [Avro Schema Parser](https://github.com/asyncapi/avro-schema-parser).\n","fullExample":"resources/casestudies/adeo/asyncapi.yaml"},"additionalResources":"Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo."}] \ No newline at end of file diff --git a/public/casestudies/adeo.yml b/config/casestudies/adeo.yml similarity index 91% rename from public/casestudies/adeo.yml rename to config/casestudies/adeo.yml index 366d416a4bb6..b50b1eb62cdf 100644 --- a/public/casestudies/adeo.yml +++ b/config/casestudies/adeo.yml @@ -1,14 +1,15 @@ +id: adeogroup company: name: Adeo Group - description: Owning different brands in retail industry focused on home improvement and DIY markets, like Leroy Merlin. + description: Adeo owns different brands in retail industry focused on home improvement and DIY markets, like Leroy Merlin. customers: 500M industry: Retail - revenue: 25.6B EURO turnover, including 768M EURO online + revenue: 25.6B EURO turnover, including 768M EURO online. website: https://www.adeo.com/ - logo: https://www.adeo.com/assets-adeo/themes/adeo-refonte/dist/assets/images/logo-adeo-v3.svg + logo: /img/casestudies/adeo/logo.svg contact: - name: Ludovic Dussart - twitter: ldussart + link: https://twitter.com/ldussart challenges: | Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. @@ -93,7 +94,7 @@ schemas: storage: Git repository where source code is. During release they are published to Confluent Schema Registry. registry: Confluent Schema Registry. versioning: Versioning is based on git tags. Schema version pushed to Confluent Schema Registry match the git tag version of the product. Every schema has a `version` information, that match with product tag version. - validation: Based on validation on a broker level using Confluent Schema Registry. + validation: Based on validation using Confluent Schema Registry. asyncapi: usecase: | Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API. @@ -101,13 +102,21 @@ asyncapi: versions: - 2.4.0 storage: Git repository where source code is. - maintainers: - Developers + maintainers: Developers editing: IntelliJ without any special plugins. Sometimes people use AsyncAPI Studio, but not regularly because of lack of support for references to local drive. audience: internal: true external: false - extensions: null + extensions: | + Extensions are used to describe details about custom security: + ```yml + x-sasl.jaas.config: >- + org.apache.kafka.common.security.plain.PlainLoginModule required + username="" password=""; + x-security.protocol: SASL_SSL + x-ssl.endpoint.identification.algorithm: https + x-sasl.mechanism: PLAIN + ```` documentation: | Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. Publishing is part of CI/CD pipeline for the product using GithubActions. @@ -254,15 +263,9 @@ asyncapi: $ref: "https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc" ``` - tools: - generator: - templates: - '@asyncapi/html': - parameters: - - sidebarOrganization=byTags - - version - parser: - plugins: - - avro-schema-parser - fullExample: https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml + tools: | + * [AsyncAPI Generator](https://github.com/asyncapi/generator): + * [HTML Template](https://github.com/asyncapi/html-template) with parameters like `sidebarOrganization=byTags` and `version`. + * [AsyncAPI JavaScript Parser](https://github.com/asyncapi/parser-js) with [Avro Schema Parser](https://github.com/asyncapi/avro-schema-parser). + fullExample: resources/casestudies/adeo/asyncapi.yaml additionalResources: Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9c4e2da810a2..6370f18ba351 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "moment": "^2.29.4", "monaco-editor": "^0.20.0", "next": "^12.0.0", + "next-mdx-remote": "^3.0.0", "node-fetch": "^2.6.7", "postcss": "^8.4.14", "react": "^17.0.2", @@ -49,7 +50,7 @@ "react-ga": "^3.1.2", "react-gtm-module": "^2.0.11", "react-scrollspy": "^3.4.2", - "react-syntax-highlighter": "^15.5.0", + "react-syntax-highlighter": "12.2.1", "react-text-truncate": "^0.16.0", "react-twitter-embed": "^3.0.3", "react-typing-animation": "^1.6.2", @@ -358,11 +359,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", - "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", + "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", "dependencies": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -413,37 +414,37 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz", + "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dependencies": { - "@babel/types": "^7.19.4" + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -477,13 +478,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", + "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -503,9 +504,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", - "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -549,11 +550,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -571,31 +572,31 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", - "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.7.tgz", + "integrity": "sha512-xueOL5+ZKX2dJbg8z8o4f4uTRTqGDRjilva9D1hiRlayJbTY8jBRL+Ph67IeRTIE439/VifHk+Z4g0SwRtQE0A==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.4", + "@babel/generator": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.4", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -604,9 +605,9 @@ } }, "node_modules/@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -864,9 +865,9 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -921,6 +922,52 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/@mdx-js/mdx/node_modules/mdast-util-to-hast": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "dependencies": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/@mdx-js/react": { "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", @@ -1327,6 +1374,32 @@ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "optional": true, + "peer": true + }, + "node_modules/@types/react": { + "version": "17.0.52", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.52.tgz", + "integrity": "sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==", + "optional": true, + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "optional": true, + "peer": true + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -2125,6 +2198,17 @@ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, + "node_modules/clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "optional": true, + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -2536,6 +2620,13 @@ "node": ">=8.0.0" } }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", + "optional": true, + "peer": true + }, "node_modules/d3": { "version": "7.6.1", "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", @@ -3282,6 +3373,12 @@ "node": ">=0.4.0" } }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true + }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -3569,6 +3666,15 @@ "is-symbol": "^1.0.2" } }, + "node_modules/esbuild": { + "version": "0.12.29", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", + "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -4263,6 +4369,15 @@ "node": ">=4" } }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "optional": true, + "dependencies": { + "delegate": "^3.1.2" + } + }, "node_modules/google-auth-library": { "version": "7.14.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", @@ -4331,6 +4446,9 @@ "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" } }, "node_modules/gtoken": { @@ -4516,9 +4634,10 @@ } }, "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "version": "9.15.10", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", + "integrity": "sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==", + "deprecated": "Version no longer supported. Upgrade to @latest", "engines": { "node": "*" } @@ -5426,16 +5545,12 @@ } }, "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.12.1.tgz", + "integrity": "sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w==", "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "fault": "^1.0.2", + "highlight.js": "~9.15.0" } }, "node_modules/lru-cache": { @@ -5541,25 +5656,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-hast": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", - "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-to-string": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", @@ -5831,6 +5927,21 @@ } } }, + "node_modules/next-mdx-remote": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-3.0.8.tgz", + "integrity": "sha512-WFSxt0crxG5PN/0WvaunzxzqV3wh3dPBZyhkclxwyQfLSRKzsNSArzot/4gYTOOZ/GtyRfNjbI/HtDsW2S4fqQ==", + "dependencies": { + "@mdx-js/mdx": "^1.6.22", + "@mdx-js/react": "^1.6.22", + "esbuild": "^0.12.9", + "pkg-dir": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.x <=17.x", + "react-dom": ">=16.x <=17.x" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -6120,6 +6231,20 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", @@ -6258,6 +6383,68 @@ "node": ">=0.10.0" } }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, "node_modules/point-in-polygon": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", @@ -7153,15 +7340,15 @@ } }, "node_modules/react-syntax-highlighter": { - "version": "15.5.0", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", - "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "version": "12.2.1", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz", + "integrity": "sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA==", "dependencies": { "@babel/runtime": "^7.3.1", - "highlight.js": "^10.4.1", - "lowlight": "^1.17.0", - "prismjs": "^1.27.0", - "refractor": "^3.6.0" + "highlight.js": "~9.15.1", + "lowlight": "1.12.1", + "prismjs": "^1.8.4", + "refractor": "^2.4.1" }, "peerDependencies": { "react": ">= 0.14.0" @@ -7311,25 +7498,53 @@ "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" }, "node_modules/refractor": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", - "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-2.10.1.tgz", + "integrity": "sha512-Xh9o7hQiQlDbxo5/XkOX6H+x/q8rmlmZKr97Ie1Q8ZM32IRRd3B/UxuA/yXDW79DBSXGWxm2yRTbcTVmAciJRw==", "dependencies": { - "hastscript": "^6.0.0", - "parse-entities": "^2.0.0", - "prismjs": "~1.27.0" + "hastscript": "^5.0.0", + "parse-entities": "^1.1.2", + "prismjs": "~1.17.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/refractor/node_modules/hastscript": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", + "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==", + "dependencies": { + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/refractor/node_modules/parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "node_modules/refractor/node_modules/prismjs": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", - "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", - "engines": { - "node": ">=6" + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", + "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", + "optionalDependencies": { + "clipboard": "^2.0.0" } }, "node_modules/regenerator-runtime": { @@ -7469,7 +7684,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" }, - "node_modules/remark-parse": { + "node_modules/remark-mdx/node_modules/remark-parse": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", @@ -7718,8 +7933,17 @@ "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" } }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==", + "optional": true + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -7972,7 +8196,10 @@ "node_modules/strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/strip-color": { "version": "0.1.0", @@ -8270,6 +8497,12 @@ "xtend": "~4.0.1" } }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "optional": true + }, "node_modules/tiny-merge-patch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tiny-merge-patch/-/tiny-merge-patch-0.1.2.tgz", @@ -8772,6 +9005,17 @@ "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/z-schema": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.2.tgz", @@ -9070,11 +9314,11 @@ } }, "@babel/generator": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", - "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", + "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", "requires": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" } @@ -9110,31 +9354,31 @@ } }, "@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz", + "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/helper-plugin-utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" }, "@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "requires": { - "@babel/types": "^7.19.4" + "@babel/types": "^7.20.2" } }, "@babel/helper-split-export-declaration": { @@ -9156,13 +9400,13 @@ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", + "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/highlight": { @@ -9176,9 +9420,9 @@ } }, "@babel/parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", - "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==" + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==" }, "@babel/plugin-proposal-object-rest-spread": { "version": "7.12.1", @@ -9207,11 +9451,11 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/runtime": { @@ -9223,36 +9467,36 @@ } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/traverse": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", - "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.7.tgz", + "integrity": "sha512-xueOL5+ZKX2dJbg8z8o4f4uTRTqGDRjilva9D1hiRlayJbTY8jBRL+Ph67IeRTIE439/VifHk+Z4g0SwRtQE0A==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.4", + "@babel/generator": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.4", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "requires": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -9465,9 +9709,9 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "requires": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -9512,6 +9756,46 @@ "unified": "9.2.0", "unist-builder": "2.0.3", "unist-util-visit": "2.0.3" + }, + "dependencies": { + "mdast-util-to-hast": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "requires": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + } + } } }, "@mdx-js/react": { @@ -9777,6 +10061,32 @@ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "optional": true, + "peer": true + }, + "@types/react": { + "version": "17.0.52", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.52.tgz", + "integrity": "sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==", + "optional": true, + "peer": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "optional": true, + "peer": true + }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -10489,6 +10799,17 @@ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, + "clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -10847,6 +11168,13 @@ "css-tree": "^1.1.2" } }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", + "optional": true, + "peer": true + }, "d3": { "version": "7.6.1", "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", @@ -11465,6 +11793,12 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true + }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -11717,6 +12051,11 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.12.29", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", + "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==" + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -12374,6 +12713,15 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "optional": true, + "requires": { + "delegate": "^3.1.2" + } + }, "google-auth-library": { "version": "7.14.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", @@ -12601,9 +12949,9 @@ } }, "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + "version": "9.15.10", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", + "integrity": "sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==" }, "html-void-elements": { "version": "1.0.5", @@ -13425,12 +13773,12 @@ } }, "lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.12.1.tgz", + "integrity": "sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w==", "requires": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" + "fault": "^1.0.2", + "highlight.js": "~9.15.0" } }, "lru-cache": { @@ -13520,21 +13868,6 @@ "unist-util-visit": "^2.0.0" } }, - "mdast-util-to-hast": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", - "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", - "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, "mdast-util-to-string": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", @@ -13765,6 +14098,17 @@ "use-sync-external-store": "1.2.0" } }, + "next-mdx-remote": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-3.0.8.tgz", + "integrity": "sha512-WFSxt0crxG5PN/0WvaunzxzqV3wh3dPBZyhkclxwyQfLSRKzsNSArzot/4gYTOOZ/GtyRfNjbI/HtDsW2S4fqQ==", + "requires": { + "@mdx-js/mdx": "^1.6.22", + "@mdx-js/react": "^1.6.22", + "esbuild": "^0.12.9", + "pkg-dir": "^5.0.0" + } + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -14020,6 +14364,14 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", @@ -14147,6 +14499,46 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" }, + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "requires": { + "find-up": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, "point-in-polygon": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", @@ -14803,15 +15195,15 @@ } }, "react-syntax-highlighter": { - "version": "15.5.0", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", - "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "version": "12.2.1", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz", + "integrity": "sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA==", "requires": { "@babel/runtime": "^7.3.1", - "highlight.js": "^10.4.1", - "lowlight": "^1.17.0", - "prismjs": "^1.27.0", - "refractor": "^3.6.0" + "highlight.js": "~9.15.1", + "lowlight": "1.12.1", + "prismjs": "^1.8.4", + "refractor": "^2.4.1" } }, "react-test-renderer": { @@ -14961,19 +15353,46 @@ "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" }, "refractor": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", - "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-2.10.1.tgz", + "integrity": "sha512-Xh9o7hQiQlDbxo5/XkOX6H+x/q8rmlmZKr97Ie1Q8ZM32IRRd3B/UxuA/yXDW79DBSXGWxm2yRTbcTVmAciJRw==", "requires": { - "hastscript": "^6.0.0", - "parse-entities": "^2.0.0", - "prismjs": "~1.27.0" + "hastscript": "^5.0.0", + "parse-entities": "^1.1.2", + "prismjs": "~1.17.0" }, "dependencies": { + "hastscript": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", + "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==", + "requires": { + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + } + }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "prismjs": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", - "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", + "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", + "requires": { + "clipboard": "^2.0.0" + } } } }, @@ -15103,32 +15522,32 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "requires": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + } } } }, - "remark-parse": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", - "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", - "requires": { - "ccount": "^1.0.0", - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^2.0.0", - "vfile-location": "^3.0.0", - "xtend": "^4.0.1" - } - }, "remark-slug": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-6.1.0.tgz", @@ -15320,6 +15739,12 @@ "kind-of": "^6.0.0" } }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==", + "optional": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -15559,7 +15984,7 @@ "strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" }, "strip-color": { "version": "0.1.0", @@ -15781,6 +16206,12 @@ "xtend": "~4.0.1" } }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "optional": true + }, "tiny-merge-patch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tiny-merge-patch/-/tiny-merge-patch-0.1.2.tgz", @@ -16192,6 +16623,11 @@ "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, "z-schema": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.2.tgz", diff --git a/package.json b/package.json index 337f7a7ebe5e..2e82a4b0a7de 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "generate:dashboard": "node scripts/dashboard/build-dashboard.js", "generate:videos": "node scripts/build-newsroom-videos.js", "generate:tools": "node scripts/build-tools.js", + "generate:casestudies": "node scripts/casestudies/index.js", "test": "echo \"No tests configured yet\"", "release": "echo \"No release to npm for this project\"" }, @@ -68,6 +69,7 @@ "moment": "^2.29.4", "monaco-editor": "^0.20.0", "next": "^12.0.0", + "next-mdx-remote": "^3.0.0", "node-fetch": "^2.6.7", "postcss": "^8.4.14", "react": "^17.0.2", @@ -75,7 +77,7 @@ "react-ga": "^3.1.2", "react-gtm-module": "^2.0.11", "react-scrollspy": "^3.4.2", - "react-syntax-highlighter": "^15.5.0", + "react-syntax-highlighter": "12.2.1", "react-text-truncate": "^0.16.0", "react-twitter-embed": "^3.0.3", "react-typing-animation": "^1.6.2", diff --git a/pages/_app.js b/pages/_app.js index c724c998e9be..db6cc9ab856e 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -3,7 +3,7 @@ import Layout from '../components/layout/Layout' import Banner from '../components/campaigns/Banner' import AppContext from '../context/AppContext' import Footer from "../components/Footer"; -import MDXProvider from '../components/MDX'; +import { MDXProvider } from '../components/MDX'; import AlgoliaSearch from '../components/AlgoliaSearch'; import '../styles/globals.css' diff --git a/pages/casestudies/[id].js b/pages/casestudies/[id].js new file mode 100644 index 000000000000..c765796b7829 --- /dev/null +++ b/pages/casestudies/[id].js @@ -0,0 +1,298 @@ +import React from 'react'; +import Link from 'next/link'; +import Paragraph from '../../components/typography/Paragraph'; +import GenericLayout from '../../components/layout/GenericLayout'; +import Heading from '../../components/typography/Heading'; +import CaseStudiesList from '../../config/case-studies.json'; +import { MDXRemote } from 'next-mdx-remote'; +import { getMDXComponents } from '../../components/MDX.js'; +import { serialize } from 'next-mdx-remote/serialize' + +export async function getStaticProps({ params }) { + const data = CaseStudiesList.filter((p) => p.id === params.id); + + return { + props: { + casestudy: data[0], + challenges: await serialize(data[0].challenges), + solution: await serialize(data[0].solution), + usecase: await serialize(data[0].asyncapi.usecase), + architecture: await serialize(data[0].technical.architecture), + testing: await serialize(data[0].technical.testing), + codegen: await serialize(data[0].technical.codegen), + schemaDesc: await serialize(data[0].schemas.description), + schemaStorage: await serialize(data[0].schemas.storage), + registry: await serialize(data[0].schemas.registry), + versioning: await serialize(data[0].schemas.versioning), + validation: await serialize(data[0].schemas.validation), + asyncapiStorage: await serialize(data[0].asyncapi.storage), + asyncapiEditing: await serialize(data[0].asyncapi.editing), + asyncapiExtensions: await serialize(data[0].asyncapi.extensions), + asyncapiDocumentation: await serialize(data[0].asyncapi.documentation), + asyncapiBindings: await serialize(data[0].asyncapi.bindings.kafka), + asyncapiTools: await serialize(data[0].asyncapi.tools), + additionalResources: await serialize(data[0].additionalResources), + }, + }; +} + +export async function getStaticPaths() { + const paths = CaseStudiesList.map((study) => ({ + params: { id: study.id }, + })); + return { + paths, + fallback: false, + }; +} + +function Index({ + casestudy, + challenges, + solution, + architecture, + testing, + codegen, + usecase, + schemaDesc, + schemaStorage, + registry, + versioning, + validation, + asyncapiStorage, + asyncapiEditing, + asyncapiExtensions, + asyncapiDocumentation, + asyncapiBindings, + asyncapiTools, + additionalResources +}) { + const image = '/img/social/website-card.png'; + const allComponents = getMDXComponents(); + return ( + +
+
+
+ + {casestudy.company.name} + +
+ + Industry: {casestudy.company.industry} + + + Customers: {casestudy.company.customers} + + + Revenue: {casestudy.company.revenue} + +
+
+ + {casestudy.company.description} + + + tl;dr just go and have a look at + + + full production-used AsyncAPI document + + + +
+
+
+ {casestudy.company.name} +
+
+
+ + Challenges + + + + +
+
+ + Solution + + + + +
+
+ + Use case + + + + +
+
+ + More details + +
+ + Languages: {casestudy.technical.languages[0]} + + + Frameworks: {casestudy.technical.frameworks[0]} + + + Protocols: {casestudy.technical.protocols[0]} + +
+ + Testing strategy + + + + +
+
+ + Approach to code generation + + + + +
+
+ + Architecture + + + + +
+
+ + More details about AsyncAPI + +
+ + Versions: {casestudy.asyncapi.versions[0]} + + + Who maintains documents: {casestudy.asyncapi.maintainers} + + + Internal users: {casestudy.asyncapi.audience.internal.toString()} + + + External users: {casestudy.asyncapi.audience.external.toString()} + +
+ + How AsyncAPI documents are stored + + + + + + Where maintainers edit AsyncAPI documents + + + + + + What extensions are used + + + + + + How documentation is generated + + + + + + What bindings are used + + + + + + What tools are used + + + + +
+
+ + Schemas + +
+ + Spec: {casestudy.schemas.description} + +
+ + Storage strategy + + + + + + Schema Registry + + + + + + Versioning of schemas + + + + + + Validation of message schemas + + + + + + Additional resources + + + + +
+ +
+
+
+ + Contact points for more details: + + +
+
+
+ ); +} + +export default Index; diff --git a/pages/casestudies/index.js b/pages/casestudies/index.js new file mode 100644 index 000000000000..eb1aa03a6986 --- /dev/null +++ b/pages/casestudies/index.js @@ -0,0 +1,49 @@ +import GenericLayout from "../../components/layout/GenericLayout"; +import CaseStudyCard from '../../components/CaseStudyCard'; +import Paragraph from '../../components/typography/Paragraph'; +import TextLink from '../../components/typography/TextLink'; +import Heading from "../../components/typography/Heading"; +import CaseStudiesList from "../../config/case-studies.json"; + +export default function casestudies() { + const description = + "Learn about different case studies based on AsyncAPI spec and related tools."; + const image = "/img/social/meetings.png"; + const title = "Case Studies"; + + return ( + + +
+
+
+ + {title} + + + The best way to learn how to use AsyncAPI is not only through documentation that usually is focused on recommendations and best practices. + It is also good to confront with real-life case studies that explain how people really use AsyncAPI and what are their flows. + + + Feel free to submit your case study. We have a template for you. For more details + + read our FAQ + . + +
+
+
+ +
+
+
+ ); +} + + diff --git a/public/img/casestudies/adeo/logo.svg b/public/img/casestudies/adeo/logo.svg new file mode 100644 index 000000000000..7c45b428b3ed --- /dev/null +++ b/public/img/casestudies/adeo/logo.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/scripts/casestudies/index.js b/scripts/casestudies/index.js new file mode 100644 index 000000000000..fbc5b610c7b1 --- /dev/null +++ b/scripts/casestudies/index.js @@ -0,0 +1,28 @@ +const { readdir, writeFile, readFile } = require('fs').promises; +const { convertToJson } = require('../utils'); +const { resolve } = require('path'); + +const dirWithCaseStudy = 'config/casestudies'; + +const buildCaseStudiesList = async () => { + let files = await readdir(dirWithCaseStudy); + let caseStudiesList = []; + try { + for (let file of files) { + const caseStudyFileName = [dirWithCaseStudy, file].join('/'); + const caseStudyContent = await readFile(caseStudyFileName, 'utf-8'); + const jsonContent = convertToJson(caseStudyContent); + + caseStudiesList.push(jsonContent); + await writeFile( + resolve(__dirname, '../../config', 'case-studies.json'), + JSON.stringify(caseStudiesList) + ) + } + } catch (err) { + console.log(err); + throw err + } +}; + +buildCaseStudiesList(); \ No newline at end of file diff --git a/scripts/tools/tools-object.js b/scripts/tools/tools-object.js index 326dff389391..f8be8aa15af0 100644 --- a/scripts/tools/tools-object.js +++ b/scripts/tools/tools-object.js @@ -2,10 +2,10 @@ const schema = require("./tools-schema.json"); const axios = require('axios') const Ajv = require("ajv") const Fuse = require("fuse.js") -const yaml = require('yaml'); const { categoryList } = require("./categorylist") const ajv = new Ajv() const validate = ajv.compile(schema) +const { convertToJson } = require('../utils'); const options = { includeScore: true, @@ -93,17 +93,4 @@ async function convertTools(data) { return appendData; } -function convertToJson(contentYAMLorJSON) { - //Axios handles conversion to JSON by default, if data returned for the server allows it - //So if returned content is not string (not YAML) we just return JSON back - if (typeof contentYAMLorJSON !== "string") return contentYAMLorJSON; - - //in some cases json can be passed here as string as it failed parsing to json because of json related error - //instead of passint it to yaml parser, return same stuff that came in so it fails on JSON Schema validation later - if (contentYAMLorJSON.trimLeft().startsWith('{')) return contentYAMLorJSON - - return yaml.parse(contentYAMLorJSON); -} - -module.exports = { convertTools } diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 000000000000..b869aa421fa5 --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,16 @@ +const yaml = require('yaml'); + +function convertToJson(contentYAMLorJSON) { + + //Axios handles conversion to JSON by default, if data returned for the server allows it + //So if returned content is not string (not YAML) we just return JSON back + if (typeof contentYAMLorJSON !== "string") return contentYAMLorJSON; + + //in some cases json can be passed here as string as it failed parsing to json because of json related error + //instead of passint it to yaml parser, return same stuff that came in so it fails on JSON Schema validation later + if (contentYAMLorJSON.trimLeft().startsWith('{')) return contentYAMLorJSON + + return yaml.parse(contentYAMLorJSON); + } + + module.exports = { convertToJson } \ No newline at end of file From dd0f14d013e4b9333cefc46d7e6eaa884874a2c4 Mon Sep 17 00:00:00 2001 From: derberg Date: Sat, 24 Dec 2022 15:31:50 +0100 Subject: [PATCH 15/28] remove link from logo from landing page --- components/CaseStudyCard.js | 2 -- pages/casestudies/[id].js | 2 -- 2 files changed, 4 deletions(-) diff --git a/components/CaseStudyCard.js b/components/CaseStudyCard.js index 8231bed97158..242a43c81eac 100644 --- a/components/CaseStudyCard.js +++ b/components/CaseStudyCard.js @@ -12,13 +12,11 @@ export default function CaseStudyCard({
- {study.company.name} - { study.company.description } diff --git a/pages/casestudies/[id].js b/pages/casestudies/[id].js index c765796b7829..910cda25b9ab 100644 --- a/pages/casestudies/[id].js +++ b/pages/casestudies/[id].js @@ -20,7 +20,6 @@ export async function getStaticProps({ params }) { architecture: await serialize(data[0].technical.architecture), testing: await serialize(data[0].technical.testing), codegen: await serialize(data[0].technical.codegen), - schemaDesc: await serialize(data[0].schemas.description), schemaStorage: await serialize(data[0].schemas.storage), registry: await serialize(data[0].schemas.registry), versioning: await serialize(data[0].schemas.versioning), @@ -54,7 +53,6 @@ function Index({ testing, codegen, usecase, - schemaDesc, schemaStorage, registry, versioning, From 6a13b8c5da68196c126a465b8b1c9176bf0ced24 Mon Sep 17 00:00:00 2001 From: derberg Date: Wed, 1 Mar 2023 15:16:17 +0100 Subject: [PATCH 16/28] case studies build like posts.json --- config/case-studies.json | 1 - scripts/casestudies/index.js | 6 ++---- scripts/index.js | 2 ++ 3 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 config/case-studies.json diff --git a/config/case-studies.json b/config/case-studies.json deleted file mode 100644 index fb646bbcfd6b..000000000000 --- a/config/case-studies.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":"adeogroup","company":{"name":"Adeo Group","description":"Adeo owns different brands in retail industry focused on home improvement and DIY markets, like Leroy Merlin.","customers":"500M","industry":"Retail","revenue":"25.6B EURO turnover, including 768M EURO online.","website":"https://www.adeo.com/","logo":"/img/casestudies/adeo/logo.svg","contact":[{"name":"Ludovic Dussart","link":"https://twitter.com/ldussart"}]},"challenges":"Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy.\n\nInitial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel.\n\nThere was a need for a standart way of describing event-driven architecture.\n","solution":"The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API.\n\nPayloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs.\n\nShift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files.\n","technical":{"languages":["Java"],"frameworks":["Spring"],"protocols":["Kafka"],"brokers":"Kafka with Kafka Connect component. There are 15 production brokers with 47 topics.","testing":"For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro.","architecture":"The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied:\n- [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html)\n Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel.\n \n Example description of response channel:\n ```\n description: >\n This topic is used to REPLY Costing Requests and is targeted by the\n `REPLY_TOPIC` header.\n ```\n- [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html)\n Info that needs to be provided by client, so producer knows where to send a response. Information is sent in message header with `REPLY_TOPIC` property. In the AsyncAPI file, information is documented as part of Message Header object.\n \n Example of request message header with `REPLY_TOPIC`:\n ```\n headers:\n type: object\n required:\n - REPLY_TOPIC\n properties:\n REPLY_TOPIC:\n $ref: \"#/components/schemas/ReplyTopic\"\n ```\n- [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html)\n This pattern enables identification of request given response was sent to. \n \n `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property.\n This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply.\n \n Example of request message header with `REQUEST_ID`:\n ```\n headers:\n type: object\n required:\n - REQUEST_ID\n properties:\n REQUEST_ID:\n $ref: \"#/components/schemas/RequestId\"\n ```\n\n Example of how `correlationId` points to `REQUEST_ID`:\n ```\n description: >\n This correlation ID is used for message tracing and messages\n correlation.\n This correlation ID is generated at runtime based on the `REQUEST_ID`\n and sent to the RESPONSE message.\n location: $message.header#/REQUEST_ID\n ````\n- [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html)\n Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events.\n- [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html)\n Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events.\n\n![Architecture Diagram](/img/casestudies/adeo/architecture.webp)\n","codegen":"Java models generation. Avro schemas used as a source.\n"},"schemas":{"description":"Avro 1.9","storage":"Git repository where source code is. During release they are published to Confluent Schema Registry.","registry":"Confluent Schema Registry.","versioning":"Versioning is based on git tags. Schema version pushed to Confluent Schema Registry match the git tag version of the product. Every schema has a `version` information, that match with product tag version.","validation":"Based on validation using Confluent Schema Registry."},"asyncapi":{"usecase":"Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API.\nThe goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company.\n","versions":["2.4.0"],"storage":"Git repository where source code is.","maintainers":"Developers","editing":"IntelliJ without any special plugins. Sometimes people use AsyncAPI Studio, but not regularly because of lack of support for references to local drive.","audience":{"internal":true,"external":false},"extensions":"Extensions are used to describe details about custom security:\n```yml\n x-sasl.jaas.config: >-\n org.apache.kafka.common.security.plain.PlainLoginModule required\n username=\"\" password=\"\";\n x-security.protocol: SASL_SSL\n x-ssl.endpoint.identification.algorithm: https\n x-sasl.mechanism: PLAIN\n````\n","documentation":"Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. \nPublishing is part of CI/CD pipeline for the product using GithubActions.\n\nRelated Maven configuration used to trigger docs generation with AsyncAPI Generator industry:\n\n```\n \n generate-asyncapi-doc\n \n \n \n com.github.eirslett\n frontend-maven-plugin\n \n ${frontend-maven-plugin.version}\n \n v12.18.4\n ${node.installation.path}\n ${node.installation.path}\n \n \n \n install node and npm\n \n install-node-and-npm\n \n generate-resources\n \n \n install @asyncapi/generator globally\n \n npm\n \n \n install @asyncapi/generator@${asyncapi.generator.version}\n \n \n \n \n \n org.codehaus.mojo\n exec-maven-plugin\n 1.6.0\n\n \n \n execute-generation\n \n exec\n \n generate-resources\n\n \n \n ${node.modules.installation.path}/${ag.binary.name}\n \n ${project.basedir}/src/docs/asyncapi/asyncapi.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p\n version=${project.version} -o ${asyncapi.generation.dir}\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-resources-plugin\n \n \n copy-resources\n \n generate-resources\n \n copy-resources\n \n \n ${asyncapi.generation.dir}/assets\n \n \n src/docs/asyncapi/assets\n true\n \n \n \n \n \n \n \n \n \n```\n\nCritical features of AsyncAPI related to documentation:\n- `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs\n- examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated\n- `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI\n","bindings":{"kafka":"All Kafka bindings are used. Server, channel, operation and message bindings.\n\nExample of server bindings:\n```\nbindings:\n kafka:\n schema.registry.url: >-\n https://schema-registry.prod.url/\n```\n\nExample of channel bindings:\n```\nbindings:\n kafka:\n replicas: 3\n partitions: 3\n cleanup.policy: delete\n retention.ms: 7 days\n```\n\nExample of operation bindings:\n```\nbindings:\n kafka:\n groupId:\n type: string\n description: >\n The groupId must be prefixed by your `svc` account, deliver by the\n Adeo Kafka team.\n This `svc` must have the write access to the topic.\n value.subject.name.strategy:\n type: string\n description: >\n We use the RecordNameStrategy to infer the messages schema.\n Use\n `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy`\n in your producer configuration.\n```\n\nExample of message bindings:\n```\nbindings:\n kafka:\n key:\n $ref: \"https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc\"\n```\n"},"tools":"* [AsyncAPI Generator](https://github.com/asyncapi/generator):\n * [HTML Template](https://github.com/asyncapi/html-template) with parameters like `sidebarOrganization=byTags` and `version`.\n* [AsyncAPI JavaScript Parser](https://github.com/asyncapi/parser-js) with [Avro Schema Parser](https://github.com/asyncapi/avro-schema-parser).\n","fullExample":"resources/casestudies/adeo/asyncapi.yaml"},"additionalResources":"Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo."}] \ No newline at end of file diff --git a/scripts/casestudies/index.js b/scripts/casestudies/index.js index fbc5b610c7b1..6a1a1f20e48c 100644 --- a/scripts/casestudies/index.js +++ b/scripts/casestudies/index.js @@ -4,7 +4,7 @@ const { resolve } = require('path'); const dirWithCaseStudy = 'config/casestudies'; -const buildCaseStudiesList = async () => { +module.exports = async function buildCaseStudiesList() { let files = await readdir(dirWithCaseStudy); let caseStudiesList = []; try { @@ -23,6 +23,4 @@ const buildCaseStudiesList = async () => { console.log(err); throw err } -}; - -buildCaseStudiesList(); \ No newline at end of file +}; \ No newline at end of file diff --git a/scripts/index.js b/scripts/index.js index 51b461380a5b..a429e1c19822 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -1,6 +1,7 @@ const rssFeed = require('./build-rss'); const buildRoadMap = require('./build-roadmap'); const buildPostList = require('./build-post-list'); +const buildCaseStudiesList = require('./casestudies'); async function start() { buildRoadMap(); @@ -17,6 +18,7 @@ async function start() { 'AsyncAPI Initiative Jobs Board', 'jobs/rss.xml' ); + await buildCaseStudiesList(); } start(); From 91c422fb14aa935659bd6e989e63bcf80aedd6f6 Mon Sep 17 00:00:00 2001 From: derberg Date: Wed, 1 Mar 2023 15:16:38 +0100 Subject: [PATCH 17/28] add case-studies.json to git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4ac2ba9a7c21..ca480b929c86 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ node_modules .swc out config/posts.json +config/case-studies.json public/rss.xml .env.local roadmap.json From 2e4bbfad59c9d308a7c4fe21b5d7cbd6886d9e8a Mon Sep 17 00:00:00 2001 From: derberg Date: Wed, 1 Mar 2023 15:34:20 +0100 Subject: [PATCH 18/28] update case study yaml template --- casestudy_template.yml | 27 +++++++++++---------------- config/casestudies/adeo.yml | 8 +++++--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/casestudy_template.yml b/casestudy_template.yml index e4c4f34f502a..8c9d58fdd81a 100644 --- a/casestudy_template.yml +++ b/casestudy_template.yml @@ -1,4 +1,5 @@ -company: #This is optional, if company wants to provide case study data anonymously, they can definitely do it +id: #Unique id that will be used on website. So in case of id that is "test" people will navigate to /casestudies/test to read about "test" case study +company: #We need basic information about a company so people know the case study is real and creditable name: #Company name. description: #Shor description of most important things people should know about the company. customers: #Amount of customers to indicate the impact that the company has. @@ -11,19 +12,19 @@ company: #This is optional, if company wants to provide case study data anonymou #- validate long term if case study is still validate #- ping you when we know we plan to change some parts of AsyncAPI that you are already using - name: #full name of the contact person - twitter: #specify if twitter, linkedin, email, so we can nicely render + link: #specify where people can find contact person challenges: | #Describe want challanges company have, that faced them towards AsyncAPI. There is no limit here. Use [CommonMark](https://commonmark.org/). solution: | #Describe the solution for the challange and what role AsyncAPI played in it. technical: - language: + languages: - #Provide a list of programming lanugages people using AsyncAPI work on frameworks: - #Specify what frameworks they use, maybe Spring in Java or Nest.js in JavaScript - protocol: + protocols: - #What protocols are used by the company in relation to AsyncAPI - broker: | + brokers: | #Provide some details about the broker that is used at the company and what is the setup, size, like of example cluster size, number of topics (in case of Kafka for example). testing: | #Explain how testing of the solution is done. How you ensure API works as expected after changes, that users are not affected. For example how do you know who will be affected if you stop sending some message? Provide examples if possible. @@ -44,8 +45,10 @@ asyncapi: - #Specify a list of versions of AsyncAPI that you use. It can be a list, some teams might use different versions. storage: | #Describe where you store AsyncAPI files and who maintains them on daily basis - editing: + editing: | #Specify how you edit AsyncAPI files, where do you edit them. Be very specific on IDEs and plugins names, if used. + maintainers: | + #share information who is a primary maintainer of AsyncAPI documents, Developer? Tech writers? Product owners? audience: #Specify if AsyncAPI audience is internal or external or both internal: true external: false @@ -56,15 +59,7 @@ asyncapi: bindings: kafka: | #Specify what bindings are used by their name and provide description on what level you use bindings. Provide examples if possible. - tools: #specify what AsyncAPI tools you use, as detailed as possible - generator: - templates: - '@asyncapi/html': - parameters: - - sidebarOrganization=byTags - - version - parser: - plugins: - - avro-schema-parser + tools: | + #specify what AsyncAPI tools you use, as detailed as possible fullExample: #should be url to full example of the case study. So later in the UI we can show it as "https://studio.asyncapi.com/?url=https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml" so the example can be previewed in the AsyncAPI Studio easily. additionalResources: #provide some additional resources if there are such. Just block of text with links to articles or videos about the case study. \ No newline at end of file diff --git a/config/casestudies/adeo.yml b/config/casestudies/adeo.yml index b50b1eb62cdf..946e8f459600 100644 --- a/config/casestudies/adeo.yml +++ b/config/casestudies/adeo.yml @@ -29,8 +29,10 @@ technical: - Spring protocols: - Kafka - brokers: Kafka with Kafka Connect component. There are 15 production brokers with 47 topics. - testing: For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. + brokers: | + Kafka with Kafka Connect component. There are 15 production brokers with 47 topics. + testing: | + For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. architecture: | The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: - [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) @@ -102,8 +104,8 @@ asyncapi: versions: - 2.4.0 storage: Git repository where source code is. - maintainers: Developers editing: IntelliJ without any special plugins. Sometimes people use AsyncAPI Studio, but not regularly because of lack of support for references to local drive. + maintainers: Developers audience: internal: true external: false From dca43b9b283706136890ce0e900a7287f6dcaa50 Mon Sep 17 00:00:00 2001 From: derberg Date: Wed, 1 Mar 2023 17:51:46 +0100 Subject: [PATCH 19/28] add schema --- casestudy_template.yml | 45 +++-- config/casestudies/adeo.yml | 3 +- pages/casestudies/[id].js | 2 +- scripts/casestudies/schema.json | 281 ++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 26 deletions(-) create mode 100644 scripts/casestudies/schema.json diff --git a/casestudy_template.yml b/casestudy_template.yml index 8c9d58fdd81a..c7bc312a98e7 100644 --- a/casestudy_template.yml +++ b/casestudy_template.yml @@ -1,29 +1,29 @@ -id: #Unique id that will be used on website. So in case of id that is "test" people will navigate to /casestudies/test to read about "test" case study -company: #We need basic information about a company so people know the case study is real and creditable +id: #Unique id that will be used on website. So in case of id that is "test" people will navigate to /casestudies/test to read about "test" case study. +company: #We need basic information about a company so people know the case study is real and creditable. name: #Company name. - description: #Shor description of most important things people should know about the company. + description: #Shor description of most important things people should know about the company. customers: #Amount of customers to indicate the impact that the company has. industry: #Is it retail? software development? put the same value that company has on their LinkedIn page. revenue: #Specify company earnings to indicate to case study reader how critical online operations are. website: #Link to main website. logo: #Link to logo of the company in SVG format. contact: - #Let us know who we can contact you, some generic email or just a name and link to some social media. We need this to be able tools: - #- validate long term if case study is still validate + #Let us know who we can contact you, some generic email or just a name and link to some social media. We need this to be able to: + #- validate long term if case study is still valid #- ping you when we know we plan to change some parts of AsyncAPI that you are already using - - name: #full name of the contact person - link: #specify where people can find contact person + - name: #Full name of the contact person. + link: #Specify where people can find contact person. challenges: | #Describe want challanges company have, that faced them towards AsyncAPI. There is no limit here. Use [CommonMark](https://commonmark.org/). solution: | - #Describe the solution for the challange and what role AsyncAPI played in it. -technical: + #Describe the solution for the challange and what role AsyncAPI played in it. There is no limit here. Use [CommonMark](https://commonmark.org/) +technical: #We need some more technical information related to case study. languages: - - #Provide a list of programming lanugages people using AsyncAPI work on + - #Provide a list of programming lanugages people using AsyncAPI work with. frameworks: - - #Specify what frameworks they use, maybe Spring in Java or Nest.js in JavaScript + - #Specify what frameworks they use, maybe Spring in Java or Nest.js in JavaScript. protocols: - - #What protocols are used by the company in relation to AsyncAPI + - #What protocols are used by the company in relation to AsyncAPI. brokers: | #Provide some details about the broker that is used at the company and what is the setup, size, like of example cluster size, number of topics (in case of Kafka for example). testing: | @@ -32,34 +32,33 @@ technical: #Explain the setup of the EDA architecture. Perfect woudl be if you could point what [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) you applied. Provide examples if possible. codegen: | #Explain if you use code generation, what you generate and how. Provide examples if possible. -schemas: +schemas: #It is useful and interesting to learn how teams handle messages schemas. description: #What spec and version do you use, is it JSON Schema? Avro? What version? - storage: #Where do you store your schemas + storage: #Where do you store your schemas. registry: #Are you using schema registry solution? which one? versioning: #How do you handle versioning of schemas? validation: #How do you handle events payload validation against schemas? -asyncapi: +asyncapi: #More specific details about AsyncAPI itself and how is it used. usecase: | #Even though this section might be a kind of replication of what was explained in challenges/solution, please describe in short what specific use case you found for AsyncAPI and how you implement it. versions: - #Specify a list of versions of AsyncAPI that you use. It can be a list, some teams might use different versions. storage: | - #Describe where you store AsyncAPI files and who maintains them on daily basis + #Describe where you store AsyncAPI files and who maintains them on daily basis. editing: | #Specify how you edit AsyncAPI files, where do you edit them. Be very specific on IDEs and plugins names, if used. maintainers: | #share information who is a primary maintainer of AsyncAPI documents, Developer? Tech writers? Product owners? - audience: #Specify if AsyncAPI audience is internal or external or both + audience: #Specify if AsyncAPI audience is internal or external or both. internal: true external: false extensions: | #Explain if you use spec extension and how. Provide examples if possible. documentation: | #Explain processes around documentation generated with AsyncAPI. How you do it, what AsyncAPI fields are critical for you. Provide examples if possible. - bindings: - kafka: | - #Specify what bindings are used by their name and provide description on what level you use bindings. Provide examples if possible. + bindings: | + #Specify what bindings are used by their name and provide description on what level you use bindings. Provide examples if possible. tools: | - #specify what AsyncAPI tools you use, as detailed as possible - fullExample: #should be url to full example of the case study. So later in the UI we can show it as "https://studio.asyncapi.com/?url=https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml" so the example can be previewed in the AsyncAPI Studio easily. -additionalResources: #provide some additional resources if there are such. Just block of text with links to articles or videos about the case study. \ No newline at end of file + #Specify what AsyncAPI tools you use, as detailed as possible. + fullExample: #Should be url to full example of the case study. So later in the UI we can show it as "https://studio.asyncapi.com/?url=https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/asyncapi.yaml" so the example can be previewed in the AsyncAPI Studio easily. +additionalResources: #Provide some additional resources if there are such. Just block of text with links to articles or videos about the case study. \ No newline at end of file diff --git a/config/casestudies/adeo.yml b/config/casestudies/adeo.yml index 946e8f459600..fc3b975eace0 100644 --- a/config/casestudies/adeo.yml +++ b/config/casestudies/adeo.yml @@ -216,8 +216,7 @@ asyncapi: - `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs - examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated - `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI - bindings: - kafka: | + bindings: | All Kafka bindings are used. Server, channel, operation and message bindings. Example of server bindings: diff --git a/pages/casestudies/[id].js b/pages/casestudies/[id].js index 910cda25b9ab..2cf1807e4774 100644 --- a/pages/casestudies/[id].js +++ b/pages/casestudies/[id].js @@ -28,7 +28,7 @@ export async function getStaticProps({ params }) { asyncapiEditing: await serialize(data[0].asyncapi.editing), asyncapiExtensions: await serialize(data[0].asyncapi.extensions), asyncapiDocumentation: await serialize(data[0].asyncapi.documentation), - asyncapiBindings: await serialize(data[0].asyncapi.bindings.kafka), + asyncapiBindings: await serialize(data[0].asyncapi.bindings), asyncapiTools: await serialize(data[0].asyncapi.tools), additionalResources: await serialize(data[0].additionalResources), }, diff --git a/scripts/casestudies/schema.json b/scripts/casestudies/schema.json new file mode 100644 index 000000000000..9c90421d7b49 --- /dev/null +++ b/scripts/casestudies/schema.json @@ -0,0 +1,281 @@ +{ + "id":"adeogroup", + "company":{ + "name":"Adeo Group", + "description":"Adeo owns different brands in retail industry focused on home improvement and DIY markets, like Leroy Merlin.", + "customers":"500M", + "industry":"Retail", + "revenue":"25.6B EURO turnover, including 768M EURO online.", + "website":"https://www.adeo.com/", + "logo":"/img/casestudies/adeo/logo.svg", + "contact":[ + { + "name":"Ludovic Dussart", + "link":"https://twitter.com/ldussart" + } + ] + }, + "challenges":"Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. + +Initial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel. + +There was a need for a standart way of describing event-driven architecture. +", + "solution":"The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API. + +Payloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs. + +Shift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files. +", + "technical":{ + "languages":[ + "Java" + ], + "frameworks":[ + "Spring" + ], + "protocols":[ + "Kafka" + ], + "brokers":"Kafka with Kafka Connect component. There are 15 production brokers with 47 topics.", + "testing":"For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. +", + "architecture":"The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: +- [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) + Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel. + + Example description of response channel: + ``` + description: > + This topic is used to REPLY Costing Requests and is targeted by the + `REPLY_TOPIC` header. + ``` +- [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) + Info that needs to be provided by client, so producer knows where to send a response. Information is sent in message header with `REPLY_TOPIC` property. In the AsyncAPI file, information is documented as part of Message Header object. + + Example of request message header with `REPLY_TOPIC`: + ``` + headers: + type: object + required: + - REPLY_TOPIC + properties: + REPLY_TOPIC: + $ref: \"#/components/schemas/ReplyTopic\" + ``` +- [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) + This pattern enables identification of request given response was sent to. + + `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. + This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. + + Example of request message header with `REQUEST_ID`: + ``` + headers: + type: object + required: + - REQUEST_ID + properties: + REQUEST_ID: + $ref: \"#/components/schemas/RequestId\" + ``` + + Example of how `correlationId` points to `REQUEST_ID`: + ``` + description: > + This correlation ID is used for message tracing and messages + correlation. + This correlation ID is generated at runtime based on the `REQUEST_ID` + and sent to the RESPONSE message. + location: $message.header#/REQUEST_ID + ```` +- [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) + Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. +- [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html) + Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. + +![Architecture Diagram](/img/casestudies/adeo/architecture.webp)", + "codegen":"Java models generation. Avro schemas used as a source." + }, + "schemas":{ + "description":"Avro 1.9", + "storage":"Git repository where source code is. During release they are published to Confluent Schema Registry.", + "registry":"Confluent Schema Registry.", + "versioning":"Versioning is based on git tags. Schema version pushed to Confluent Schema Registry match the git tag version of the product. Every schema has a `version` information, that match with product tag version.", + "validation":"Based on validation using Confluent Schema Registry." + }, + "asyncapi":{ + "usecase":"Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API. +The goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company.", + "versions":[ + "2.4.0" + ], + "storage":"Git repository where source code is.", + "editing":"IntelliJ without any special plugins. Sometimes people use AsyncAPI Studio, but not regularly because of lack of support for references to local drive.", + "maintainers":"Developers", + "audience":{ + "internal":false, + "external":false + }, + "extensions":"Extensions are used to describe details about custom security: +```yml + x-sasl.jaas.config: >- + org.apache.kafka.common.security.plain.PlainLoginModule required + username=\"\" password=\"\"; + x-security.protocol: SASL_SSL + x-ssl.endpoint.identification.algorithm: https + x-sasl.mechanism: PLAIN +```` +", + "documentation":"Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. +Publishing is part of CI/CD pipeline for the product using GithubActions. + +Related Maven configuration used to trigger docs generation with AsyncAPI Generator industry: + +``` + + generate-asyncapi-doc + + + + com.github.eirslett + frontend-maven-plugin + + ${frontend-maven-plugin.version} + + v12.18.4 + ${node.installation.path} + ${node.installation.path} + + + + install node and npm + + install-node-and-npm + + generate-resources + + + install @asyncapi/generator globally + + npm + + + install @asyncapi/generator@${asyncapi.generator.version} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + + execute-generation + + exec + + generate-resources + + + + ${node.modules.installation.path}/${ag.binary.name} + + ${project.basedir}/src/docs/asyncapi/asyncapi.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p + version=${project.version} -o ${asyncapi.generation.dir} + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + + generate-resources + + copy-resources + + + ${asyncapi.generation.dir}/assets + + + src/docs/asyncapi/assets + true + + + + + + + + + +``` + +Critical features of AsyncAPI related to documentation: +- `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs +- examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated +- `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI +", + "bindings":"All Kafka bindings are used. Server, channel, operation and message bindings. + +Example of server bindings: +``` +bindings: + kafka: + schema.registry.url: >- + https://schema-registry.prod.url/ +``` + +Example of channel bindings: +``` +bindings: + kafka: + replicas: 3 + partitions: 3 + cleanup.policy: delete + retention.ms: 7 days +``` + +Example of operation bindings: +``` +bindings: + kafka: + groupId: + type: string + description: > + The groupId must be prefixed by your `svc` account, deliver by the + Adeo Kafka team. + This `svc` must have the write access to the topic. + value.subject.name.strategy: + type: string + description: > + We use the RecordNameStrategy to infer the messages schema. + Use + `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy` + in your producer configuration. +``` + +Example of message bindings: +``` +bindings: + kafka: + key: + $ref: \"https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc\" +``` +", + "tools":"* [AsyncAPI Generator](https://github.com/asyncapi/generator): + * [HTML Template](https://github.com/asyncapi/html-template) with parameters like `sidebarOrganization=byTags` and `version`. +* [AsyncAPI JavaScript Parser](https://github.com/asyncapi/parser-js) with [Avro Schema Parser](https://github.com/asyncapi/avro-schema-parser). +", + "fullExample":"resources/casestudies/adeo/asyncapi.yaml" + }, + "additionalResources":"Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. - better for data interchange" +} \ No newline at end of file From f17d48eca3fd51e1f85d1105ad149f4830543fdc Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 2 Mar 2023 09:35:59 +0100 Subject: [PATCH 20/28] add readme --- README.md | 23 ++++++++++++++++++- package.json | 1 - .../casestudies/casestudy_template.yml | 0 3 files changed, 22 insertions(+), 2 deletions(-) rename casestudy_template.yml => scripts/casestudies/casestudy_template.yml (100%) diff --git a/README.md b/README.md index 0c74cc50a5d0..743eb6733b94 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,27 @@ npm run build Generated files of the website go to the `.next` folder. +## Case studies + +### Overview + +A case study is a special document that any end-user company can provide. End-user company is a company that uses AsyncAPI to solve some technical challenges. A case study is not a document where a vendor company can describe how they build their commercial AsyncAPI-based product. On the other hand, it is completely fine if a case study of some end-user mentions some commercial tools that helped them to work with AsyncAPI or event-driven architecture. An example of such a case can be a case study from an end-user where at some point, Confluent Schema Registry is mentioned in an explanation about schemas and runtime message validation. + +### How to add a case study + +A case study is documented in the form of a YAML file. Anyone can open a pull request with a new case study. + +- YAML file must be located in `config/casestudies` +- To make it easier for you to create such a YAML file you can use: + - [Template YAML with comments explaining every section](scripts/casestudies/casestudy_template.yml) + - [JSON Schema that describes all YAML fields](scripts/casestudies/schema.json) +- All additional files for the case study, like complete AsyncAPI document examples, should be located in `public/resources/casestudies` directory. +- Company logo and other images that will be rendered in the website should be located in `public/img/casestudies` + +Once you collect all information and create a case study, open a pull request. It must be authored or at least approved by a representative of the given company. Such a representative is probably already a contact person mentioned in the case study. + +A case study became publicly available right after merging and rebuilding the website. + ## JSON Schema definitions All AsyncAPI JSON Schema definition files are being served within the `/definitions/` path. The content is being served from GH, in particular from https://github.com/asyncapi/spec-json-schemas/tree/master/schemas. @@ -105,7 +126,7 @@ This repository has the following structure: └── tailwind.config.js # TailwindCSS configuration file ``` -## Contributors ✨ +## Contributors Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): diff --git a/package.json b/package.json index 2e82a4b0a7de..88f3d8b26c6b 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "generate:dashboard": "node scripts/dashboard/build-dashboard.js", "generate:videos": "node scripts/build-newsroom-videos.js", "generate:tools": "node scripts/build-tools.js", - "generate:casestudies": "node scripts/casestudies/index.js", "test": "echo \"No tests configured yet\"", "release": "echo \"No release to npm for this project\"" }, diff --git a/casestudy_template.yml b/scripts/casestudies/casestudy_template.yml similarity index 100% rename from casestudy_template.yml rename to scripts/casestudies/casestudy_template.yml From 4cb0ffd8bb6dfcec96a4a699384d33fb226f01a0 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Wed, 8 Mar 2023 20:19:12 +0100 Subject: [PATCH 21/28] Apply suggestions from language review Co-authored-by: Alejandra Quetzalli Co-authored-by: Ludovic Dussart --- README.md | 10 +++--- config/casestudies/adeo.yml | 32 ++++++++++--------- .../resources/casestudies/adeo/asyncapi.yaml | 4 +-- scripts/casestudies/casestudy_template.yml | 12 +++---- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 0a4603ecf816..3d8048d12292 100644 --- a/README.md +++ b/README.md @@ -88,22 +88,22 @@ Generated files of the website go to the `.next` folder. ### Overview -A case study is a special document that any end-user company can provide. End-user company is a company that uses AsyncAPI to solve some technical challenges. A case study is not a document where a vendor company can describe how they build their commercial AsyncAPI-based product. On the other hand, it is completely fine if a case study of some end-user mentions some commercial tools that helped them to work with AsyncAPI or event-driven architecture. An example of such a case can be a case study from an end-user where at some point, Confluent Schema Registry is mentioned in an explanation about schemas and runtime message validation. +A case study is a special document that any end-user company can provide. An end-user company is a company that uses AsyncAPI to solve technical challenges. A case study is not a document where a vendor company can describe how they build their commercial AsyncAPI-based product. On the other hand, it is completely fine if a case study of some end-user mentions some commercial tools that helped them to work with AsyncAPI or event-driven architecture. An example of such a case can be a case study from an end-user where at some point, Confluent Schema Registry is mentioned in an explanation about schemas and runtime message validation. ### How to add a case study A case study is documented in the form of a YAML file. Anyone can open a pull request with a new case study. -- YAML file must be located in `config/casestudies` +- YAML file must be located in `config/casestudies`. - To make it easier for you to create such a YAML file you can use: - [Template YAML with comments explaining every section](scripts/casestudies/casestudy_template.yml) - [JSON Schema that describes all YAML fields](scripts/casestudies/schema.json) -- All additional files for the case study, like complete AsyncAPI document examples, should be located in `public/resources/casestudies` directory. -- Company logo and other images that will be rendered in the website should be located in `public/img/casestudies` +- All additional files for the case study, like complete AsyncAPI document examples, should be located in the `public/resources/casestudies` directory. +- Company logo and other images that will be rendered in the website should be located in `public/img/casestudies`. Once you collect all information and create a case study, open a pull request. It must be authored or at least approved by a representative of the given company. Such a representative is probably already a contact person mentioned in the case study. -A case study became publicly available right after merging and rebuilding the website. +A case study becomes publicly available right after merging and rebuilding the website. ## JSON Schema definitions diff --git a/config/casestudies/adeo.yml b/config/casestudies/adeo.yml index fc3b975eace0..82c0a79bf82d 100644 --- a/config/casestudies/adeo.yml +++ b/config/casestudies/adeo.yml @@ -11,15 +11,15 @@ company: - name: Ludovic Dussart link: https://twitter.com/ldussart challenges: | - Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. + Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations globally. Different business units use different information systems. It is hard to learn how each business unit shares information about its systems, API and accuracy. - Initial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel. + The initial solution was a developer portal with a list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel. There was a need for a standart way of describing event-driven architecture. solution: | - The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API. + The API is now described with AsyncAPI. The AsyncAPI file, stored with the source code, generates HTML documentation in the same release pipeline for the product. Documentation is exposed internally as part of the product for other company units depending on the API. - Payloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs. + Payloads are described with Avro schema. These schemas generate models and are referenced directly in AsyncAPI files thanks to the `schemaFormat` feature and `$ref`. This way, they make sure the code is aligned with the docs. Shift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files. technical: @@ -32,7 +32,7 @@ technical: brokers: | Kafka with Kafka Connect component. There are 15 production brokers with 47 topics. testing: | - For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. + For Kafka, e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with the [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. architecture: | The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: - [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) @@ -45,7 +45,7 @@ technical: `REPLY_TOPIC` header. ``` - [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) - Info that needs to be provided by client, so producer knows where to send a response. Information is sent in message header with `REPLY_TOPIC` property. In the AsyncAPI file, information is documented as part of Message Header object. + Info that needs to be provided by the client so producer knows where to send a response. Information is sent in the message header with the `REPLY_TOPIC` property. The AsyncAPI file documents information as part of the Message Header object. Example of request message header with `REPLY_TOPIC`: ``` @@ -58,9 +58,9 @@ technical: $ref: "#/components/schemas/ReplyTopic" ``` - [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) - This pattern enables identification of request given response was sent to. + This pattern enables the identification of the request given to the sent response. - `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. + The `REQUEST_ID` property is in the request message header. The `CORRELATION_ID` property is in the response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. Example of request message header with `REQUEST_ID`: @@ -76,6 +76,7 @@ technical: Example of how `correlationId` points to `REQUEST_ID`: ``` + correlationId: description: > This correlation ID is used for message tracing and messages correlation. @@ -84,9 +85,9 @@ technical: location: $message.header#/REQUEST_ID ```` - [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) - Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. + Also known as Dead Letter Queue. In Kafka, it is just another channel where undelivered messages are sent. Not part of the AsyncAPI file, as API consumers will not listen to this channel. Consumers know what happens with wrong events. - [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html) - Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. + Invalid messages are routed to the dedicated channel for rejected requests but are not part of the AsyncAPI file, as API consumers will not listen to this channel. Consumers know what happens with wrong events. ![Architecture Diagram](/img/casestudies/adeo/architecture.webp) codegen: | @@ -95,12 +96,12 @@ schemas: description: Avro 1.9 storage: Git repository where source code is. During release they are published to Confluent Schema Registry. registry: Confluent Schema Registry. - versioning: Versioning is based on git tags. Schema version pushed to Confluent Schema Registry match the git tag version of the product. Every schema has a `version` information, that match with product tag version. + versioning: Versioning is based on git tags. The schema version pushed to Confluent Schema Registry matches the git tag version of the product. Every schema has a `version` information that matches with product tag version. validation: Based on validation using Confluent Schema Registry. asyncapi: usecase: | - Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API. - The goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company. + Document the API of the product, so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from a machine-readable document that describes the API. + The goal was to document API in a standardized way, so other internal products could follow to unify how APIs are documented across the company. versions: - 2.4.0 storage: Git repository where source code is. @@ -120,8 +121,8 @@ asyncapi: x-sasl.mechanism: PLAIN ```` documentation: | - Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. - Publishing is part of CI/CD pipeline for the product using GithubActions. + Documentation generated from AsyncAPI is hosted as part of the product on a dedicated endpoint using Spring controller. + Publishing is part of the CI/CD pipeline for the product using GithubActions. Related Maven configuration used to trigger docs generation with AsyncAPI Generator industry: @@ -213,6 +214,7 @@ asyncapi: ``` Critical features of AsyncAPI related to documentation: + - use of `version` parameter in the generator command to display the release version from the `Maven` pom - `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs - examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated - `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI diff --git a/public/resources/casestudies/adeo/asyncapi.yaml b/public/resources/casestudies/adeo/asyncapi.yaml index 73c0d899c0d4..d35655d1b38e 100644 --- a/public/resources/casestudies/adeo/asyncapi.yaml +++ b/public/resources/casestudies/adeo/asyncapi.yaml @@ -1,9 +1,9 @@ asyncapi: 2.4.0 info: - title: Adeo Async API Case Study + title: Adeo AsyncAPI Case Study version: "%REPLACED_BY_MAVEN%" description: > - This Adeo specification illustrates how ADEO uses Async API to document some of their exchanges + This Adeo specification illustrates how ADEO uses AsyncAPI to document some of their exchanges. contact: name: AsyncApi team email: case-study@asyncapi.com diff --git a/scripts/casestudies/casestudy_template.yml b/scripts/casestudies/casestudy_template.yml index c7bc312a98e7..e53f58701dce 100644 --- a/scripts/casestudies/casestudy_template.yml +++ b/scripts/casestudies/casestudy_template.yml @@ -25,11 +25,11 @@ technical: #We need some more technical information related to case study. protocols: - #What protocols are used by the company in relation to AsyncAPI. brokers: | - #Provide some details about the broker that is used at the company and what is the setup, size, like of example cluster size, number of topics (in case of Kafka for example). + #Provide some details about the broker that is used at the company and what is the setup, size, example cluster size, and a number of topics (in the case of Kafka for example). testing: | - #Explain how testing of the solution is done. How you ensure API works as expected after changes, that users are not affected. For example how do you know who will be affected if you stop sending some message? Provide examples if possible. + #Explain how testing of the solution is done. How do you ensure an API works as expected after changes and that users are unaffected? For example, how do you know who will be affected if you stop sending some messages? Provide examples if possible. architecture: | - #Explain the setup of the EDA architecture. Perfect woudl be if you could point what [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) you applied. Provide examples if possible. + #Explain the setup of the EDA architecture. It would be perfect if you could point out what [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) you applied. Provide examples if possible. codegen: | #Explain if you use code generation, what you generate and how. Provide examples if possible. schemas: #It is useful and interesting to learn how teams handle messages schemas. @@ -46,16 +46,16 @@ asyncapi: #More specific details about AsyncAPI itself and how is it used. storage: | #Describe where you store AsyncAPI files and who maintains them on daily basis. editing: | - #Specify how you edit AsyncAPI files, where do you edit them. Be very specific on IDEs and plugins names, if used. + #Specify how you edit AsyncAPI files and where you edit them. Be very specific on IDEs and plugin names if used. maintainers: | - #share information who is a primary maintainer of AsyncAPI documents, Developer? Tech writers? Product owners? + #Share information; who is the primary maintainer of AsyncAPI documents? Developers? Tech writers? Product owners? audience: #Specify if AsyncAPI audience is internal or external or both. internal: true external: false extensions: | #Explain if you use spec extension and how. Provide examples if possible. documentation: | - #Explain processes around documentation generated with AsyncAPI. How you do it, what AsyncAPI fields are critical for you. Provide examples if possible. + #Explain processes around documentation generated with AsyncAPI. How do you do it? What AsyncAPI fields are critical for you? Provide examples if possible. bindings: | #Specify what bindings are used by their name and provide description on what level you use bindings. Provide examples if possible. tools: | From c9726ae5b13733e94698f136327d5d6111fdc93c Mon Sep 17 00:00:00 2001 From: Alejandra Quetzalli Date: Wed, 8 Mar 2023 17:06:58 -0800 Subject: [PATCH 22/28] Update public/resources/casestudies/adeo/asyncapi.yaml --- public/resources/casestudies/adeo/asyncapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/resources/casestudies/adeo/asyncapi.yaml b/public/resources/casestudies/adeo/asyncapi.yaml index d35655d1b38e..7779478c8b8c 100644 --- a/public/resources/casestudies/adeo/asyncapi.yaml +++ b/public/resources/casestudies/adeo/asyncapi.yaml @@ -5,7 +5,7 @@ info: description: > This Adeo specification illustrates how ADEO uses AsyncAPI to document some of their exchanges. contact: - name: AsyncApi team + name: AsyncAPI Community email: case-study@asyncapi.com servers: production: From 66b2ce7606ca19f378d62aaad2427618a4fcac5a Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 9 Mar 2023 10:08:06 +0100 Subject: [PATCH 23/28] fix formatting --- config/casestudies/adeo.yml | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/config/casestudies/adeo.yml b/config/casestudies/adeo.yml index 82c0a79bf82d..f81f6f8fa739 100644 --- a/config/casestudies/adeo.yml +++ b/config/casestudies/adeo.yml @@ -36,18 +36,14 @@ technical: architecture: | The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: - [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) - Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel. - - Example description of response channel: + Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel. Example description of response channel: ``` description: > This topic is used to REPLY Costing Requests and is targeted by the `REPLY_TOPIC` header. ``` - [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) - Info that needs to be provided by the client so producer knows where to send a response. Information is sent in the message header with the `REPLY_TOPIC` property. The AsyncAPI file documents information as part of the Message Header object. - - Example of request message header with `REPLY_TOPIC`: + Info that needs to be provided by the client so producer knows where to send a response. Information is sent in the message header with the `REPLY_TOPIC` property. The AsyncAPI file documents information as part of the Message Header object. Example of request message header with `REPLY_TOPIC`: ``` headers: type: object @@ -58,12 +54,8 @@ technical: $ref: "#/components/schemas/ReplyTopic" ``` - [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) - This pattern enables the identification of the request given to the sent response. - - The `REQUEST_ID` property is in the request message header. The `CORRELATION_ID` property is in the response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. - This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. - - Example of request message header with `REQUEST_ID`: + This pattern enables the identification of the request given to the sent response. The `REQUEST_ID` property is in the request message header. The `CORRELATION_ID` property is in the response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. + This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. Example of request message header with `REQUEST_ID`: ``` headers: type: object @@ -73,16 +65,15 @@ technical: REQUEST_ID: $ref: "#/components/schemas/RequestId" ``` - Example of how `correlationId` points to `REQUEST_ID`: ``` - correlationId: - description: > - This correlation ID is used for message tracing and messages - correlation. - This correlation ID is generated at runtime based on the `REQUEST_ID` - and sent to the RESPONSE message. - location: $message.header#/REQUEST_ID + correlationId: + description: > + This correlation ID is used for message tracing and messages + correlation. + This correlation ID is generated at runtime based on the `REQUEST_ID` + and sent to the RESPONSE message. + location: $message.header#/REQUEST_ID ```` - [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) Also known as Dead Letter Queue. In Kafka, it is just another channel where undelivered messages are sent. Not part of the AsyncAPI file, as API consumers will not listen to this channel. Consumers know what happens with wrong events. From 261e86919be8d88619bd26ef96a0e990023f5c8d Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 9 Mar 2023 10:11:27 +0100 Subject: [PATCH 24/28] fix json schema --- scripts/casestudies/schema.json | 238 ++------------------------------ 1 file changed, 9 insertions(+), 229 deletions(-) diff --git a/scripts/casestudies/schema.json b/scripts/casestudies/schema.json index 9c90421d7b49..883cc20b2da2 100644 --- a/scripts/casestudies/schema.json +++ b/scripts/casestudies/schema.json @@ -15,18 +15,8 @@ } ] }, - "challenges":"Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy. - -Initial solution was a developer portal with list of all applications and reference to dedicated documentation. Some legacy systems had docs written in MS Excel. - -There was a need for a standart way of describing event-driven architecture. -", - "solution":"The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API. - -Payloads are described with Avro schema. These schemas are used to generate models and they are also referenced directly in AsyncAPI files thanks to `schemaFormat` feature and `$ref`. This way they make sure code is aligned with docs. - -Shift to using AsyncAPI also enables the team to implement more use cases using AsyncAPI files. -", + "challenges":"Cost Component Repository product, part of the ADEO tech products, is used to calculate and publish transfer prices between different internal locations on the globe. Different business units use different information systems. It is hard to learn how each business unit share information about their systems, their API and accuracy.", + "solution":"The API is now described with AsyncAPI. AsyncAPI file, stored together with the source code, is used to generate HTML documentation in the same release pipeline for the product. Documentation is exposed as part of the product internally for other company units depending on the API.", "technical":{ "languages":[ "Java" @@ -38,63 +28,8 @@ Shift to using AsyncAPI also enables the team to implement more use cases using "Kafka" ], "brokers":"Kafka with Kafka Connect component. There are 15 production brokers with 47 topics.", - "testing":"For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro. -", - "architecture":"The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied: -- [Request/Reply](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html) - Described with `description` field in AsyncAPI. Reply goes to dedicated reply channel. - - Example description of response channel: - ``` - description: > - This topic is used to REPLY Costing Requests and is targeted by the - `REPLY_TOPIC` header. - ``` -- [Return Address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) - Info that needs to be provided by client, so producer knows where to send a response. Information is sent in message header with `REPLY_TOPIC` property. In the AsyncAPI file, information is documented as part of Message Header object. - - Example of request message header with `REPLY_TOPIC`: - ``` - headers: - type: object - required: - - REPLY_TOPIC - properties: - REPLY_TOPIC: - $ref: \"#/components/schemas/ReplyTopic\" - ``` -- [Correlation Identifier](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html) - This pattern enables identification of request given response was sent to. - - `REQUEST_ID` property is in the request message header. `CORRELATION_ID` property is in response message header. Both headers are described in the AsyncAPI Message Header object and referred to in the AsyncAPI `correlationID` property. - This means that correlation identifier is represented by different property in the message header, depending if it is a request or reply. - - Example of request message header with `REQUEST_ID`: - ``` - headers: - type: object - required: - - REQUEST_ID - properties: - REQUEST_ID: - $ref: \"#/components/schemas/RequestId\" - ``` - - Example of how `correlationId` points to `REQUEST_ID`: - ``` - description: > - This correlation ID is used for message tracing and messages - correlation. - This correlation ID is generated at runtime based on the `REQUEST_ID` - and sent to the RESPONSE message. - location: $message.header#/REQUEST_ID - ```` -- [DeadLetter Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html) - Also known as Dead Letter Queue. In Kafka it is just another channel where undelivered messages are sent. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. -- [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html) - Invalid messages are routed to dedicated channel for rejected requests. Not part of AsyncAPI file as API consumers will not listen to this channel. Consumers just know what happens with wrong events. - -![Architecture Diagram](/img/casestudies/adeo/architecture.webp)", + "testing":"For Kafka e2e tests are done with [Zerocode](https://github.com/authorjapps/zerocode). Load tests are handled with [JMeter](https://jmeter.apache.org/) with [kloadgen](https://github.com/corunet/kloadgen) plugin that supports Kafka and Avro.", + "architecture":"The following [enterprise integration patterns](https://www.enterpriseintegrationpatterns.com/patterns/messaging) are applied.", "codegen":"Java models generation. Avro schemas used as a source." }, "schemas":{ @@ -105,8 +40,7 @@ Shift to using AsyncAPI also enables the team to implement more use cases using "validation":"Based on validation using Confluent Schema Registry." }, "asyncapi":{ - "usecase":"Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API. -The goal was to document API in a standardized way, so other internal products could follow to unify the way APIs are documented across the company.", + "usecase":"Document the API of the product so its users know how it works and how to use it. AsyncAPI was selected as the standard that allows you to generate documentation from machine-readable document that describes the API.", "versions":[ "2.4.0" ], @@ -117,164 +51,10 @@ The goal was to document API in a standardized way, so other internal products c "internal":false, "external":false }, - "extensions":"Extensions are used to describe details about custom security: -```yml - x-sasl.jaas.config: >- - org.apache.kafka.common.security.plain.PlainLoginModule required - username=\"\" password=\"\"; - x-security.protocol: SASL_SSL - x-ssl.endpoint.identification.algorithm: https - x-sasl.mechanism: PLAIN -```` -", - "documentation":"Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller. -Publishing is part of CI/CD pipeline for the product using GithubActions. - -Related Maven configuration used to trigger docs generation with AsyncAPI Generator industry: - -``` - - generate-asyncapi-doc - - - - com.github.eirslett - frontend-maven-plugin - - ${frontend-maven-plugin.version} - - v12.18.4 - ${node.installation.path} - ${node.installation.path} - - - - install node and npm - - install-node-and-npm - - generate-resources - - - install @asyncapi/generator globally - - npm - - - install @asyncapi/generator@${asyncapi.generator.version} - - - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - - execute-generation - - exec - - generate-resources - - - - ${node.modules.installation.path}/${ag.binary.name} - - ${project.basedir}/src/docs/asyncapi/asyncapi.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p - version=${project.version} -o ${asyncapi.generation.dir} - - - - - - - org.apache.maven.plugins - maven-resources-plugin - - - copy-resources - - generate-resources - - copy-resources - - - ${asyncapi.generation.dir}/assets - - - src/docs/asyncapi/assets - true - - - - - - - - - -``` - -Critical features of AsyncAPI related to documentation: -- `descriptions` that support `CommonMark` (Markdown) as they allow to put detailed structured descriptions and screenshots inside generated docs -- examples and validation information. In this case converted from Avro to JSON Schema to show it in documentation and have examples generated -- `Tags` for tagging operations to categorize them to make it easier to navigate in documentation UI -", - "bindings":"All Kafka bindings are used. Server, channel, operation and message bindings. - -Example of server bindings: -``` -bindings: - kafka: - schema.registry.url: >- - https://schema-registry.prod.url/ -``` - -Example of channel bindings: -``` -bindings: - kafka: - replicas: 3 - partitions: 3 - cleanup.policy: delete - retention.ms: 7 days -``` - -Example of operation bindings: -``` -bindings: - kafka: - groupId: - type: string - description: > - The groupId must be prefixed by your `svc` account, deliver by the - Adeo Kafka team. - This `svc` must have the write access to the topic. - value.subject.name.strategy: - type: string - description: > - We use the RecordNameStrategy to infer the messages schema. - Use - `value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy` - in your producer configuration. -``` - -Example of message bindings: -``` -bindings: - kafka: - key: - $ref: \"https://deploy-preview-921--asyncapi-website.netlify.app/resources/casestudies/adeo/CostingResponseKey.avsc\" -``` -", - "tools":"* [AsyncAPI Generator](https://github.com/asyncapi/generator): - * [HTML Template](https://github.com/asyncapi/html-template) with parameters like `sidebarOrganization=byTags` and `version`. -* [AsyncAPI JavaScript Parser](https://github.com/asyncapi/parser-js) with [Avro Schema Parser](https://github.com/asyncapi/avro-schema-parser). -", + "extensions":"Extensions are used to describe details about custom security.", + "documentation":"Documentation generated from AsyncAPI is hosted as part of the product on dedicated endpoint using Spring controller.", + "bindings":"All Kafka bindings are used. Server, channel, operation and message bindings.", + "tools":"[AsyncAPI Generator](https://github.com/asyncapi/generator) and [HTML Template](https://github.com/asyncapi/html-template) with parameters like `sidebarOrganization=byTags` and `version`.", "fullExample":"resources/casestudies/adeo/asyncapi.yaml" }, "additionalResources":"Watch [this video presentation about AsyncAPI case study](https://www.youtube.com/watch?v=WwhRbvrf6Rs) from Ludovic Dussart, Ineat & Antoine Delequeuche, Adeo. - better for data interchange" From 4eed4caa03f3753fc71f6b1e00ffcbf7988d5055 Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 9 Mar 2023 10:14:01 +0100 Subject: [PATCH 25/28] fix html metadata --- pages/casestudies/[id].js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/casestudies/[id].js b/pages/casestudies/[id].js index 2cf1807e4774..191b5d7b335f 100644 --- a/pages/casestudies/[id].js +++ b/pages/casestudies/[id].js @@ -69,8 +69,8 @@ function Index({ const allComponents = getMDXComponents(); return ( Date: Wed, 15 Mar 2023 15:41:02 +0530 Subject: [PATCH 26/28] UI changes done --- components/CaseStudyCard.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/CaseStudyCard.js b/components/CaseStudyCard.js index 242a43c81eac..fc741b4c487b 100644 --- a/components/CaseStudyCard.js +++ b/components/CaseStudyCard.js @@ -7,13 +7,13 @@ export default function CaseStudyCard({ return null; } return ( -
+
{studies.map((study, index) => ( -
- +
+ {study.company.name} From 6dfa5d15f537cfde60a265af960297a3216e80de Mon Sep 17 00:00:00 2001 From: derberg Date: Thu, 16 Mar 2023 19:32:47 +0100 Subject: [PATCH 27/28] update nav, and making sure not new mermaid is used --- components/navigation/otherItems.js | 2 +- package-lock.json | 578 ++++++++++++++++------------ package.json | 2 +- 3 files changed, 333 insertions(+), 249 deletions(-) diff --git a/components/navigation/otherItems.js b/components/navigation/otherItems.js index 8ce757ecae47..6f18e3245105 100644 --- a/components/navigation/otherItems.js +++ b/components/navigation/otherItems.js @@ -1,6 +1,6 @@ export default [ + { text: "Case Studies", href: '/casestudies' }, { text: 'Blog', href: '/blog' }, // { text: 'Shop', href: 'https://asyncapi.threadless.com', target: '_blank' }, { text: "Roadmap", href: '/roadmap', className: 'text-secondary-500 font-bold' }, - { text: "Jobs", href: '/jobs', className: 'text-secondary-500 font-bold' }, ] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bcbd63aa143d..60c3e797a956 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "lodash": "^4.17.21", "markdown-to-txt": "^2.0.1", "markdown-toc": "1.2.0", - "mermaid": "^9.3.0", + "mermaid": "9.3.0", "moment": "^2.29.4", "monaco-editor": "^0.20.0", "next": "^12.0.0", @@ -1023,6 +1023,51 @@ "resolved": "https://registry.npmjs.org/@next/mdx/-/mdx-9.5.5.tgz", "integrity": "sha512-IdwwXrxqNhZuEeyVdCKtv1R2CqIsAZm/rtCUbE5WImFW3VK8nrSFDFi+JkoWoDcHgGgaJYgfoYAoieHV5POGJg==" }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", + "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-android-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", + "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", + "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@next/swc-darwin-x64": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", @@ -1038,6 +1083,141 @@ "node": ">= 10" } }, + "node_modules/@next/swc-freebsd-x64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", + "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm-gnueabihf": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", + "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", + "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", + "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", + "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", + "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", + "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", + "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", + "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2263,14 +2443,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, - "node_modules/cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "dependencies": { - "layout-base": "^1.0.0" - } - }, "node_modules/cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -2560,53 +2732,6 @@ "optional": true, "peer": true }, - "node_modules/cytoscape": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.23.0.tgz", - "integrity": "sha512-gRZqJj/1kiAVPkrVFvz/GccxsXhF3Qwpptl32gKKypO4IlqnKBjTOu+HbXtEggSGzC5KCaHp3/F7GgENrtsFkA==", - "dependencies": { - "heap": "^0.2.6", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "dependencies": { - "cose-base": "^1.0.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "dependencies": { - "cose-base": "^2.2.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "dependencies": { - "layout-base": "^2.0.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" - }, "node_modules/d3": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", @@ -2648,9 +2773,9 @@ } }, "node_modules/d3-array": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", - "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", "dependencies": { "internmap": "1 - 2" }, @@ -2701,9 +2826,9 @@ } }, "node_modules/d3-contour": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", - "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", "dependencies": { "d3-array": "^3.2.0" }, @@ -2807,9 +2932,9 @@ } }, "node_modules/d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", "dependencies": { "d3-array": "2.5.0 - 3" }, @@ -2837,9 +2962,9 @@ } }, "node_modules/d3-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", "engines": { "node": ">=12" } @@ -2904,20 +3029,20 @@ } }, "node_modules/d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "dependencies": { - "d3-path": "1 - 3" + "d3-path": "^3.1.0" }, "engines": { "node": ">=12" } }, "node_modules/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "dependencies": { "d3-array": "2 - 3" }, @@ -2978,19 +3103,14 @@ } }, "node_modules/dagre-d3-es": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz", - "integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.6.tgz", + "integrity": "sha512-CaaE/nZh205ix+Up4xsnlGmpog5GGm81Upi2+/SBHxwNwrccBb3K51LzjZ1U6hgvOlAEUsVWf1xSTzCyKpJ6+Q==", "dependencies": { - "d3": "^7.8.2", + "d3": "^7.7.0", "lodash-es": "^4.17.21" } }, - "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" - }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -3171,9 +3291,9 @@ } }, "node_modules/dompurify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz", - "integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==" }, "node_modules/domutils": { "version": "2.8.0", @@ -3215,11 +3335,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, - "node_modules/elkjs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", - "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4318,11 +4433,6 @@ "tslib": "^2.0.3" } }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" - }, "node_modules/highlight.js": { "version": "9.15.10", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", @@ -5023,11 +5133,6 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "node_modules/layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" - }, "node_modules/lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", @@ -5391,26 +5496,20 @@ } }, "node_modules/mermaid": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.4.3.tgz", - "integrity": "sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.3.0.tgz", + "integrity": "sha512-mGl0BM19TD/HbU/LmlaZbjBi//tojelg8P/mxD6pPZTAYaI+VawcyBdqRsoUHSc7j71PrMdJ3HBadoQNdvP5cg==", "dependencies": { "@braintree/sanitize-url": "^6.0.0", - "cytoscape": "^3.23.0", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.1.0", - "d3": "^7.4.0", - "dagre-d3-es": "7.0.9", - "dayjs": "^1.11.7", - "dompurify": "2.4.3", - "elkjs": "^0.8.2", + "d3": "^7.0.0", + "dagre-d3-es": "7.0.6", + "dompurify": "2.4.1", "khroma": "^2.0.0", "lodash-es": "^4.17.21", + "moment-mini": "^2.24.0", "non-layered-tidy-tree-layout": "^2.0.2", "stylis": "^4.1.2", - "ts-dedent": "^2.2.0", - "uuid": "^9.0.0", - "web-worker": "^1.2.0" + "uuid": "^9.0.0" } }, "node_modules/mermaid/node_modules/uuid": { @@ -5548,6 +5647,11 @@ "node": "*" } }, + "node_modules/moment-mini": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.29.4.tgz", + "integrity": "sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg==" + }, "node_modules/monaco-editor": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.20.0.tgz", @@ -8383,14 +8487,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", - "engines": { - "node": ">=6.10" - } - }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -8707,11 +8803,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -9641,12 +9732,84 @@ "resolved": "https://registry.npmjs.org/@next/mdx/-/mdx-9.5.5.tgz", "integrity": "sha512-IdwwXrxqNhZuEeyVdCKtv1R2CqIsAZm/rtCUbE5WImFW3VK8nrSFDFi+JkoWoDcHgGgaJYgfoYAoieHV5POGJg==" }, + "@next/swc-android-arm-eabi": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", + "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", + "optional": true + }, + "@next/swc-android-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", + "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", + "optional": true + }, + "@next/swc-darwin-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", + "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", + "optional": true + }, "@next/swc-darwin-x64": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", "optional": true }, + "@next/swc-freebsd-x64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", + "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", + "optional": true + }, + "@next/swc-linux-arm-gnueabihf": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", + "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", + "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", + "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", + "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", + "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", + "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", + "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", + "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", + "optional": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10732,14 +10895,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, - "cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "requires": { - "layout-base": "^1.0.0" - } - }, "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -10987,46 +11142,6 @@ "optional": true, "peer": true }, - "cytoscape": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.23.0.tgz", - "integrity": "sha512-gRZqJj/1kiAVPkrVFvz/GccxsXhF3Qwpptl32gKKypO4IlqnKBjTOu+HbXtEggSGzC5KCaHp3/F7GgENrtsFkA==", - "requires": { - "heap": "^0.2.6", - "lodash": "^4.17.21" - } - }, - "cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "requires": { - "cose-base": "^1.0.0" - } - }, - "cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "requires": { - "cose-base": "^2.2.0" - }, - "dependencies": { - "cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "requires": { - "layout-base": "^2.0.0" - } - }, - "layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" - } - } - }, "d3": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", @@ -11065,9 +11180,9 @@ } }, "d3-array": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", - "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", "requires": { "internmap": "1 - 2" } @@ -11103,9 +11218,9 @@ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" }, "d3-contour": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", - "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", "requires": { "d3-array": "^3.2.0" } @@ -11171,9 +11286,9 @@ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" }, "d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", "requires": { "d3-array": "2.5.0 - 3" } @@ -11192,9 +11307,9 @@ } }, "d3-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" }, "d3-polygon": { "version": "3.0.1", @@ -11238,17 +11353,17 @@ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, "d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "requires": { - "d3-path": "1 - 3" + "d3-path": "^3.1.0" } }, "d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "requires": { "d3-array": "2 - 3" } @@ -11291,19 +11406,14 @@ } }, "dagre-d3-es": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz", - "integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.6.tgz", + "integrity": "sha512-CaaE/nZh205ix+Up4xsnlGmpog5GGm81Upi2+/SBHxwNwrccBb3K51LzjZ1U6hgvOlAEUsVWf1xSTzCyKpJ6+Q==", "requires": { - "d3": "^7.8.2", + "d3": "^7.7.0", "lodash-es": "^4.17.21" } }, - "dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" - }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -11450,9 +11560,9 @@ } }, "dompurify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz", - "integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==" }, "domutils": { "version": "2.8.0", @@ -11491,11 +11601,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, - "elkjs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", - "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -12517,11 +12622,6 @@ "tslib": "^2.0.3" } }, - "heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" - }, "highlight.js": { "version": "9.15.10", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", @@ -13139,11 +13239,6 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" - }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", @@ -13484,26 +13579,20 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "mermaid": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.4.3.tgz", - "integrity": "sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.3.0.tgz", + "integrity": "sha512-mGl0BM19TD/HbU/LmlaZbjBi//tojelg8P/mxD6pPZTAYaI+VawcyBdqRsoUHSc7j71PrMdJ3HBadoQNdvP5cg==", "requires": { "@braintree/sanitize-url": "^6.0.0", - "cytoscape": "^3.23.0", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.1.0", - "d3": "^7.4.0", - "dagre-d3-es": "7.0.9", - "dayjs": "^1.11.7", - "dompurify": "2.4.3", - "elkjs": "^0.8.2", + "d3": "^7.0.0", + "dagre-d3-es": "7.0.6", + "dompurify": "2.4.1", "khroma": "^2.0.0", "lodash-es": "^4.17.21", + "moment-mini": "^2.24.0", "non-layered-tidy-tree-layout": "^2.0.2", "stylis": "^4.1.2", - "ts-dedent": "^2.2.0", - "uuid": "^9.0.0", - "web-worker": "^1.2.0" + "uuid": "^9.0.0" }, "dependencies": { "uuid": { @@ -13626,6 +13715,11 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, + "moment-mini": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.29.4.tgz", + "integrity": "sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg==" + }, "monaco-editor": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.20.0.tgz", @@ -15946,11 +16040,6 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" }, - "ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==" - }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -16193,11 +16282,6 @@ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" }, - "web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" - }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index fb7ec0eb0e90..820e37c2a595 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "lodash": "^4.17.21", "markdown-to-txt": "^2.0.1", "markdown-toc": "1.2.0", - "mermaid": "^9.3.0", + "mermaid": "9.3.0", "moment": "^2.29.4", "monaco-editor": "^0.20.0", "next": "^12.0.0", From 9ddd06b5415c734fb13151e547d2bad1e0daecdc Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Fri, 17 Mar 2023 17:18:05 +0100 Subject: [PATCH 28/28] fix link :laughing: --- pages/casestudies/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/casestudies/index.js b/pages/casestudies/index.js index eb1aa03a6986..68cec50047a9 100644 --- a/pages/casestudies/index.js +++ b/pages/casestudies/index.js @@ -31,7 +31,7 @@ export default function casestudies() { Feel free to submit your case study. We have a template for you. For more details - + read our FAQ .