Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: case studies content setup #921

Merged
merged 44 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
04adfcd
feat: case studies content setup
derberg Aug 30, 2022
3449015
anonymous note
derberg Aug 30, 2022
ec04a3c
unify source and template
derberg Aug 30, 2022
8a794bb
Merge branch 'master' into casestudy
derberg Aug 30, 2022
6e10ff8
Apply suggestions from code review
derberg Sep 6, 2022
5d6373c
added a bit more clarification from Ludovic
derberg Sep 6, 2022
ccc2183
added more details on versioning, validation and correlationId
derberg Sep 7, 2022
e4185a8
Merge branch 'master' into casestudy
derberg Sep 7, 2022
a5a034e
add examples :rocket:
derberg Sep 8, 2022
f6198cf
Merge branch 'casestudy' of https://github.com/derberg/website-asynca…
derberg Sep 8, 2022
0d287d2
Merge branch 'master' into casestudy
derberg Sep 8, 2022
7b06881
Update netlify.toml
derberg Sep 8, 2022
9c26dc3
Merge branch 'casestudy' of https://github.com/derberg/website-asynca…
derberg Sep 8, 2022
86bfbbb
Apply suggestions from code review
derberg Sep 8, 2022
6102241
add contact
derberg Sep 8, 2022
6c75b37
Merge branch 'casestudy' of https://github.com/derberg/website-asynca…
derberg Sep 8, 2022
afadc15
fix spelling mistake
derberg Sep 22, 2022
6c0e075
update maven config
derberg Sep 27, 2022
f28747f
improve structure of info basing on discussion with Maya that works o…
derberg Oct 4, 2022
8b7a049
Merge branch 'master' into casestudy
quetzalliwrites Dec 14, 2022
6f4c08c
adding simplistic UI
derberg Dec 24, 2022
dd0f14d
remove link from logo from landing page
derberg Dec 24, 2022
6a13b8c
case studies build like posts.json
derberg Mar 1, 2023
91c422f
add case-studies.json to git ignore
derberg Mar 1, 2023
2e4bbfa
update case study yaml template
derberg Mar 1, 2023
dca43b9
add schema
derberg Mar 1, 2023
f17d48e
add readme
derberg Mar 2, 2023
a397921
Merge branch 'master' into casestudy
derberg Mar 2, 2023
7a3955f
Merge branch 'master' into casestudy
quetzalliwrites Mar 8, 2023
ac1708b
Merge branch 'master' into casestudy
quetzalliwrites Mar 8, 2023
4cb0ffd
Apply suggestions from language review
derberg Mar 8, 2023
31a4a7d
Merge branch 'master' into casestudy
quetzalliwrites Mar 9, 2023
c9726ae
Update public/resources/casestudies/adeo/asyncapi.yaml
quetzalliwrites Mar 9, 2023
66b2ce7
fix formatting
derberg Mar 9, 2023
261e869
fix json schema
derberg Mar 9, 2023
4eed4ca
fix html metadata
derberg Mar 9, 2023
d77a8ae
Merge branch 'master' into casestudy
quetzalliwrites Mar 10, 2023
80e7195
Merge branch 'master' into casestudy
derberg Mar 14, 2023
3c0d518
UI changes done
akshatnema Mar 15, 2023
86e3207
Merge branch 'master' into casestudy
derberg Mar 16, 2023
6dfa5d1
update nav, and making sure not new mermaid is used
derberg Mar 16, 2023
4d703e5
Merge branch 'master' into casestudy
derberg Mar 17, 2023
9ddd06b
fix link :laughing:
derberg Mar 17, 2023
183107e
Merge branch 'master' into casestudy
derberg Mar 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ node_modules
.swc
out
config/posts.json
config/case-studies.json
public/rss.xml
.env.local
yarn.lock
Expand Down
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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. 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`.
- 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 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 becomes publicly available right after merging and rebuilding the website.

## JSON Schema definitions

All AsyncAPI JSON Schema definition files are being served within the `/definitions/<file>` path. The content is being served from GH, in particular from https://github.com/asyncapi/spec-json-schemas/tree/master/schemas.
Expand Down Expand Up @@ -119,7 +140,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)):

Expand Down
29 changes: 29 additions & 0 deletions components/CaseStudyCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Paragraph from './typography/Paragraph';

export default function CaseStudyCard({
studies = []
}) {
if(studies.length === 0){
return null;
}
return (
<div className="pt-10 flex flex-wrap lg:grid lg:grid-cols-3 lg:gap-8 lg:text-center">
{studies.map((study, index) => (
<a key={index} href={`casestudies/${study.id}`}>
<div className="rounded-md border border-gray-200 overflow-hidden bg-white p-4 max-w-sm">
<span className="mr-2">
<img
className="m-auto h-16"
src={study.company.logo}
alt={study.company.name}
/>
</span>
<Paragraph typeStyle="body-md" className="my-4">
{ study.company.description }
</Paragraph>
</div>
</a>
))}
</div>
);
}
4 changes: 2 additions & 2 deletions components/MDX.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ let mermaidInitialized = false;
initializeMermaid();
const mdxComponents = getMDXComponents();

export default function MDXProvider({ children }) {
export function MDXProvider({ children }) {
return (
<CoreMDXProvider components={mdxComponents}>
{children}
</CoreMDXProvider>
);
}

function getMDXComponents() {
export function getMDXComponents() {
return {
h1: props => <h1 {...props} className={`${props.className || ''} my-4 font-heading antialiased font-semibold tracking-heading text-gray-900 text-2xl`} />,
h2: props => <h2 {...props} className={`${props.className || ''} mb-4 mt-6 font-heading antialiased font-semibold tracking-heading text-gray-900 text-2xl`} />,
Expand Down
2 changes: 1 addition & 1 deletion components/navigation/otherItems.js
Original file line number Diff line number Diff line change
@@ -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' },
]
265 changes: 265 additions & 0 deletions config/casestudies/adeo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
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 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.

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. 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 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:
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 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)
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`:
```
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 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
required:
- REQUEST_ID
properties:
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
````
- [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.
- [Invalid Message Channel](https://www.enterpriseintegrationpatterns.com/patterns/messaging/InvalidMessageChannel.html)
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: |
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. 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 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.
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
extensions: |
Extensions are used to describe details about custom security:
```yml
x-sasl.jaas.config: >-
org.apache.kafka.common.security.plain.PlainLoginModule required
username="<CLUSTER_API_KEY>" password="<CLUSTER_API_SECRET>";
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 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:

```
<profile>
<id>generate-asyncapi-doc</id>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<!-- Use the latest released version:
https://repo1.maven.org/maven2/com/github/eirslett/frontend-maven-plugin/ -->
<version>${frontend-maven-plugin.version}</version>
<configuration>
<nodeVersion>v12.18.4</nodeVersion>
<installDirectory>${node.installation.path}</installDirectory>
<workingDirectory>${node.installation.path}</workingDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<phase>generate-resources</phase>
</execution>
<execution>
<id>install @asyncapi/generator globally</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install @asyncapi/generator@${asyncapi.generator.version}</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>

<executions>
<execution>
<id>execute-generation</id>
<goals>
<goal>exec</goal>
</goals>
<phase>generate-resources</phase>

<configuration>
<!-- Access binary file in node_modules because it doesn't work on windows otherwise. -->
<executable>${node.modules.installation.path}/${ag.binary.name}</executable>
<commandlineArgs>
${project.basedir}/src/docs/asyncapi/asyncapi.yaml @asyncapi/html-template@${asyncapi.htmltemplate.version} -p sidebarOrganization=byTags -p
version=${project.version} -o ${asyncapi.generation.dir}
</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${asyncapi.generation.dir}/assets</outputDirectory>
<resources>
<resource>
<directory>src/docs/asyncapi/assets</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
```

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
derberg marked this conversation as resolved.
Show resolved Hide resolved
- 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.
1 change: 1 addition & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading