-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(docs): updating Pokeshop documentation (#1892)
* Updating Pokeshop documentation * Changing code specs in Pokeshop docs to json * standardize sidebar * Updating Add Pokemon tutorial * Adding text for list pokemon use case * Adding documentation to import pokemon * Adding case of get pokemon by id * Updating scenarios * Updating images * Apply suggestions from code review Co-authored-by: Julianne Fermi <julianne@kubeshop.io> --------- Co-authored-by: Julianne Fermi <julianne@kubeshop.io>
- Loading branch information
1 parent
6a9ba3e
commit 7b9df9e
Showing
34 changed files
with
571 additions
and
351 deletions.
There are no files selected for viewing
Binary file not shown.
File renamed without changes.
Binary file added
BIN
+141 KB
docs/docs/live-examples/pokeshop/images/add-pokemon-all-test-specs.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+499 KB
docs/docs/live-examples/pokeshop/images/add-pokemon-database-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+339 KB
docs/docs/live-examples/pokeshop/images/get-pokemon-by-id-api-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+323 KB
.../docs/live-examples/pokeshop/images/get-pokemon-by-id-db-no-query-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+420 KB
docs/docs/live-examples/pokeshop/images/get-pokemon-by-id-db-query-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+350 KB
.../docs/live-examples/pokeshop/images/get-pokemon-by-id-redis-query-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+342 KB
docs/docs/live-examples/pokeshop/images/get-pokemon-by-id-redis-set-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+144 KB
docs/docs/live-examples/pokeshop/images/get-pokemon-by-id-trace-cachehit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+321 KB
docs/docs/live-examples/pokeshop/images/get-pokemon-by-id-trace-cachemiss.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+307 KB
docs/docs/live-examples/pokeshop/images/import-pokemon-api-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+386 KB
docs/docs/live-examples/pokeshop/images/import-pokemon-db-latency-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+445 KB
...docs/live-examples/pokeshop/images/import-pokemon-message-dequeue-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+330 KB
...docs/live-examples/pokeshop/images/import-pokemon-message-enqueue-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+383 KB
docs/docs/live-examples/pokeshop/images/import-pokemon-pokeapi-call-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+343 KB
docs/docs/live-examples/pokeshop/images/list-pokemons-api-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+401 KB
docs/docs/live-examples/pokeshop/images/list-pokemons-db-latency-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+544 KB
docs/docs/live-examples/pokeshop/images/list-pokemons-db-query-test-spec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Pokeshop API | ||
|
||
As a testing ground, the team at Tracetest has implemented a sample instrumented API around the [PokeAPI](https://pokeapi.co/). | ||
|
||
The idea is to have a microservice-divided system that behaves like a typical scenario by having async processes ([RabbitMQ](https://www.rabbitmq.com/)), cache layers ([Redis](https://redis.io/)), database storage ([Postgres](https://www.postgresql.org/)), and simple CRUD interfaces for Pokemons. | ||
|
||
With this, users can get familiar with the Tracetest tool by focusing on creating assertions, visualizing the trace, and identifying the different data that comes from the Collector ([Jaeger](https://www.jaegertracing.io/)). Users will learn about basic instrumentation practices like what tools to use, what data to send, when, and what suggested standards need to be followed. | ||
|
||
- **Source Code**: https://github.com/kubeshop/pokeshop | ||
- **Running it locally**: [instructions](https://github.com/kubeshop/pokeshop/blob/master/docs/installing.md#run-it-locally) | ||
- **Running on Kubernetes**: [instructions](https://github.com/kubeshop/pokeshop/blob/master/docs/installing.md#run-on-a-kubernetes-cluster) | ||
|
||
## Use Cases | ||
|
||
We have three use cases that use each component of this structure and that can be observed via Open Telemetry and tested with Tracetest. Each one is triggered by an API call to their respective endpoint: | ||
|
||
- [Add Pokemon](./use-cases/add-pokemon.md): Add a new Pokemon only relying on user input into the database. | ||
- [List Pokemon](./use-cases/list-pokemon.md): Lists all Pokemons registered into Pokeshop. | ||
- [Import Pokemon](./use-cases/import-pokemon.md): Given a Pokemon ID, this endpoint does an async process, going to PokeAPI to get Pokemon data and adding it to the database. | ||
|
||
## System Architecture | ||
|
||
The system is divided into two components: | ||
- an **API** that serves client requests, | ||
- a **Worker** who deals with background processes. | ||
|
||
The communication between the API and Worker is made using a `RabbitMQ` queue, and both services emit telemetry data to Jaeger and communicate with a Postgres database. | ||
|
||
A diagram of the system structure can be seen here: | ||
|
||
```mermaid | ||
flowchart TD | ||
A[(Redis)] | ||
B[(Postgres)] | ||
C(Node.js API) | ||
D(RabbitMQ) | ||
E(Worker) | ||
F(Jaeger) | ||
C --> |IORedis| A | ||
C --> |Sequelize| B | ||
C --> |ampqlib| D | ||
D --> |ampqlib| E | ||
E --> |Sequelize| B | ||
C --> |OpenTelemetry Node.js SDK| F | ||
E --> |OpenTelemetry Node.js SDK| F | ||
``` | ||
|
||
In our live tests, we are deploying into a single Kubernetes namespace, deployed via a [Helm chart](https://github.com/kubeshop/pokeshop/blob/master/docs/installing.md#run-on-a-kubernetes-cluster). | ||
|
||
The Pokeshop API is only accessible from within the Kubernetes cluster network as Tracetest needs to be able to reach it. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Pokeshop API - Add Pokemon | ||
|
||
This use case showcases a simple example of an API call where call data is validated and persisted into a database. | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant Endpoint as POST /pokemon | ||
participant API as API | ||
participant Database as Postgres | ||
Endpoint->>API: request | ||
alt request is invalid | ||
API-->>Endpoint: 400 Bad Request <br> <List of errors> | ||
end | ||
API->>Database: save pokemon | ||
Database-->>API: saved pokemon | ||
API-->>Endpoint: 201 Created <br> <Pokemon object> | ||
``` | ||
|
||
You can trigger this use case by calling the endpoint `POST /pokemon`, with the following request body: | ||
```json | ||
{ | ||
"name": "meowth", | ||
"type": "normal", | ||
"imageUrl": "https://assets.pokemon.com/assets/cms2/img/pokedex/full/052.png", | ||
"isFeatured": true | ||
} | ||
``` | ||
|
||
It should return the following payload: | ||
```json | ||
{ | ||
"id": 1000, | ||
"name": "meowth", | ||
"type": "normal", | ||
"imageUrl": "https://assets.pokemon.com/assets/cms2/img/pokedex/full/052.png", | ||
"isFeatured": true | ||
} | ||
``` | ||
|
||
## Building a Test for This Scenario | ||
|
||
Using Tracetest, we can [create a test](../../../web-ui/creating-tests.md) that will execute an API call on `POST /pokemon` and validate two properties: | ||
- The API should return a proper result with **HTTP 201 Created**. | ||
- The database should return with **low latency (< 200ms)**. | ||
|
||
### Traces | ||
|
||
Running these tests for the first time will create an Observability trace like the image above, where you can see spans for the API call, validation (an API internal operation), and database calls: | ||
![](../images/add-pokemon-trace.png) | ||
|
||
### Assertions | ||
|
||
With this trace, now we can build [assertions](../../../concepts/assertions.md) on Tracetest and validate the API response and the database latency: | ||
|
||
- **The API should return a proper result with HTTP 201 Created:** | ||
![](../images/add-pokemon-api-test-spec.png) | ||
|
||
- **The database should return with low latency (< 200ms):** | ||
![](../images/add-pokemon-database-test-spec.png) | ||
|
||
Now you can validate this entire use case. | ||
|
||
### Test Definition | ||
|
||
If you want to replicate this entire test on Tracetest see by yourself, you can replicate these steps on our Web UI or using our CLI, saving the following test definition as the file `test-definition.yml` and later running: | ||
|
||
```sh | ||
tracetest test -d test-definition.yml --wait-for-results | ||
``` | ||
|
||
```yaml | ||
type: Test | ||
spec: | ||
name: Pokeshop - Add | ||
description: Add a Pokemon | ||
trigger: | ||
type: http | ||
httpRequest: | ||
url: http://demo-pokemon-api.demo/pokemon | ||
method: POST | ||
headers: | ||
- key: Content-Type | ||
value: application/json | ||
body: '{"name":"meowth","type":"normal","imageUrl":"https://assets.pokemon.com/assets/cms2/img/pokedex/full/052.png","isFeatured":true}' | ||
specs: | ||
- selector: span[tracetest.span.type="http" name="POST /pokemon" http.method="POST"] | ||
assertions: | ||
- attr:http.response.body = '{"id":6152,"imageUrl":"https://assets.pokemon.com/assets/cms2/img/pokedex/full/052.png","isFeatured":true,"type":"normal","name":"meowth"}' | ||
- attr:http.status_code = 201 | ||
- selector: span[tracetest.span.type="database"] | ||
assertions: | ||
- attr:tracetest.span.duration <= 200ms | ||
``` |
153 changes: 153 additions & 0 deletions
153
docs/docs/live-examples/pokeshop/use-cases/get-pokemon-by-id.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# Pokeshop API - Get Pokemon by ID | ||
|
||
This use case retrieves a specific Pokemon from the cache if it is cached, or from the database (Postgres) also populating the cache. The idea of this query is to showcase a straightforward scenario, where the API layer receives a request from the outside and needs to evaluate a different behavior depending of its dependencies. | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant Endpoint as GET /pokemon/:id | ||
participant API as API | ||
participant Database as Postgres | ||
participant Cache as Redis | ||
Endpoint->>API: request | ||
API->>Cache: query cache | ||
Cache-->>API: cache response | ||
alt cache hit | ||
API-->>Endpoint: 200 OK <br> <Pokemon object> | ||
else cache miss | ||
API->>Database: get specific of pokemon | ||
Database-->>API: pokemon | ||
API->>Cache: populate cache | ||
Cache-->>API: ok | ||
API-->>Endpoint: 200 OK <br> <Pokemon object> | ||
end | ||
``` | ||
|
||
You can trigger this use case by calling the endpoint `GET /pokemon/25` without payload and should receive a payload similar to this: | ||
```json | ||
{ | ||
"id": 25, | ||
"name": "pikachu", | ||
"type": "electric", | ||
"imageUrl": "https://assets.pokemon.com/assets/cms2/img/pokedex/full/025.png", | ||
"isFeatured": true | ||
} | ||
``` | ||
|
||
## Building a Test for the Described Scenarios | ||
|
||
Using Tracetest, we can [create two tests](../../../web-ui/creating-tests.md) that will execute an API call on `GET /pokemon/25` and validate the following scenarios: | ||
1. **An API call with a cache hit.** | ||
- The API should return a valid result with HTTP 200 OK. | ||
- The cache should be queried. | ||
- The database should not be queried. | ||
2. **An API call with a cache miss.** | ||
- The API should return a valid result with HTTP 200 OK. | ||
- The cache should be queried. | ||
- The cache should be populated. | ||
- The database should be queried. | ||
|
||
### Traces | ||
|
||
Running these tests for the first time will create an Observability trace with two different shapes, depending on the cache situation. | ||
|
||
1. **Cache Miss** where we can see spans from the API, database, and cache: | ||
![](../images/get-pokemon-by-id-trace-cachemiss.png) | ||
|
||
2. **Cache Hit** where we can see spans from the API and cache: | ||
![](../images/get-pokemon-by-id-trace-cachehit.png) | ||
|
||
### Assertions | ||
|
||
With this trace, we can build [assertions](../../../concepts/assertions.md) on Tracetest and validate API, cache, and database responses: | ||
|
||
- [Both Cases] The API should return a valid result with HTTP 200 OK. | ||
![](../images/get-pokemon-by-id-api-test-spec.png) | ||
|
||
- [Both Cases] The cache should be queried. | ||
![](../images/get-pokemon-by-id-redis-query-test-spec.png) | ||
|
||
- [Cache Hit] The database should not be queried. | ||
![](../images/get-pokemon-by-id-db-no-query-test-spec.png) | ||
|
||
- [Cache Miss] The cache should be populated. | ||
![](../images/get-pokemon-by-id-redis-set-test-spec.png) | ||
|
||
- [Cache Miss] The database should be queried. | ||
![](../images/get-pokemon-by-id-db-query-test-spec.png) | ||
|
||
Now you can validate this entire use case. | ||
|
||
### Test Definition | ||
|
||
If you want to replicate those tests on Tracetest, you can replicate these steps on our Web UI or using our CLI, saving one of the test definitions as the file `test-definition.yml` and running: | ||
|
||
```sh | ||
tracetest test -d test-definition.yml --wait-for-results | ||
``` | ||
|
||
#### Cache Miss Scenario | ||
|
||
```yaml | ||
type: Test | ||
spec: | ||
name: Pokeshop - Get Pokemon by ID [cache miss scenario] | ||
trigger: | ||
type: http | ||
httpRequest: | ||
url: http://demo-pokemon-api.demo/pokemon/${env:POKEMON_ID} | ||
method: GET | ||
headers: | ||
- key: Content-Type | ||
value: application/json | ||
specs: | ||
- selector: span[tracetest.span.type="http" http.method="GET"] | ||
assertions: | ||
- attr:http.status_code = 200 | ||
- attr:http.response.body | json_path '$.id' = '${env:POKEMON_ID}' | ||
- selector: span[tracetest.span.type="database" db.system="redis" db.operation="get"] | ||
assertions: | ||
- attr:name = "get pokemon-${env:POKEMON_ID}" | ||
- selector: span[tracetest.span.type="database" db.system="redis" db.operation="set"] | ||
assertions: | ||
- attr:name = "set pokemon-${env:POKEMON_ID}" | ||
- selector: |- | ||
span[tracetest.span.type="database" name="findOne pokeshop.pokemon" | ||
db.system="postgres" db.name="pokeshop" db.operation="findOne" db.sql.table="pokemon"] | ||
assertions: | ||
- attr:tracetest.selected_spans.count > 0 | ||
``` | ||
|
||
#### Cache Miss Scenario | ||
|
||
```yml | ||
type: Test | ||
spec: | ||
name: Pokeshop - Get Pokemon by ID [cache hit scenario] | ||
trigger: | ||
type: http | ||
httpRequest: | ||
url: http://demo-pokemon-api.demo/pokemon/${env:POKEMON_ID} | ||
method: GET | ||
headers: | ||
- key: Content-Type | ||
value: application/json | ||
specs: | ||
- selector: span[tracetest.span.type="http" http.method="GET"] | ||
assertions: | ||
- attr:http.status_code = 200 | ||
- attr:http.response.body | json_path '$.id' = "${env:POKEMON_ID}" | ||
- selector: span[tracetest.span.type="database" db.system="redis" db.operation="get"] | ||
assertions: | ||
- attr:name = "get pokemon-${env:POKEMON_ID}" | ||
- attr:db.result | json_path '$.id' = "${env:POKEMON_ID}" | ||
- selector: |- | ||
span[tracetest.span.type="database" name="findOne pokeshop.pokemon" | ||
db.system="postgres" db.name="pokeshop" db.operation="findOne" db.sql.table="pokemon"] | ||
assertions: | ||
- attr:tracetest.selected_spans.count = 0 | ||
``` |
Oops, something went wrong.