Initial commit

This commit is contained in:
Daniel López García
2021-06-23 12:17:01 +02:00
parent 09036a8016
commit 6815c3816a
10 changed files with 804 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

+45
View File
@@ -0,0 +1,45 @@
version: "3.8"
networks:
k6:
grafana:
services:
influxdb:
image: influxdb:latest
networks:
- k6
- grafana
ports:
- "8086:8086"
environment:
- INFLUXDB_DB=k6
grafana:
image: grafana/grafana:latest
networks:
- grafana
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_BASIC_ENABLED=false
volumes:
- ./shared/grafana:/etc/grafana/provisioning/
simulado:
image : ldabiralai/simulado:latest
networks:
- k6
ports:
- "3001:80"
volumes:
- ./shared/simulado:/app
command: ./bin/simulado -f /app/mocks.json
k6:
image: loadimpact/k6:latest
networks:
- k6
ports:
- "6565:6565"
volumes:
- ./shared/k6:/scripts
environment:
- K6_OUT=influxdb=http://influxdb:8086/k6
+65
View File
@@ -0,0 +1,65 @@
openapi: 3.0.0
info:
title: existingApis
version: '1.0'
servers:
- url: 'http://localhost:3001'
paths:
'/product/{productId}/similarids':
parameters:
- schema:
type: string
name: productId
in: path
required: true
get:
operationId: get-product-similarids
summary: Gets the ids of the similar products
description: Returns the similar products to a given one ordered by similarity
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
description: List of productIds similar to a given one ordered by similarity
minItems: 0
uniqueItems: true
items:
type: integer
'/product/{productId}':
parameters:
- schema:
type: string
name: productId
in: path
required: true
get:
operationId: get-product-productId
summary: Gets a product detail
description: Returns the product detail for a given productId
responses:
'200':
description: OK
content:
application/json:
schema:
description: 'Product detail'
type: object
properties:
id:
type: string
minLength: 1
name:
type: string
minLength: 1
price:
type: number
availability:
type: boolean
required:
- id
- name
- price
- availability
+33
View File
@@ -0,0 +1,33 @@
# Backend dev technical test
We want to offer a new feature to our customers showing similar products to the one they are browsing. To do this we agreed with out front end applications to create a new REST API that will provide them the product detail of the similar products to a given one. [Here](./similarProducts.yaml) is the contract we agreed.
We already have an endpoint that provides the product Ids similar to a given one. We also have another endpoint that returns the product detail by productId. [Here](./existingApis.yaml) is the documentation of the existing APIs.
**Create a Spring boot application that exposes the agreed REST API on port 5000.**
![Diagram](./assets/diagram.jpg "Diagram")
Note that _Test_ and _Mocks_ components are given, you must only implement _yourApp_.
## Testing and Self-evaluation
You can run the same test we will put through your application. You just need to have docker installed.
First of all you may need to enable file sharing for the `shared` folder on your docker dashboard -> settings -> resources -> file sharing.
Then you can start the mocks and other needed infrastructure with the following command.
```
docker-compose up -d simulado influxdb grafana
```
Check that mocks are working with a sample request to [http://localhost:3001/product/1/similarids](http://localhost:3001/product/1/similarids).
To execute the test run:
```
docker-compose run --rm k6 run scripts/test.js
```
Browse [http://localhost:3000/d/Le2Ku9NMk/k6-performance-test](http://localhost:3000/d/Le2Ku9NMk/k6-performance-test) to view the results.
## Evaluation
The following topics will be considered:
- Code clarity and maintainability
- Performance
- Resilence
+12
View File
@@ -0,0 +1,12 @@
apiVersion: 1
providers:
- name: 'myinfluxdb'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards
@@ -0,0 +1,444 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"links": [],
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 0
},
"hiddenSeries": false,
"id": 5,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.3.6",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"groupBy": [
{
"params": [
"1s"
],
"type": "time"
},
{
"params": [
"scenario"
],
"type": "tag"
},
{
"params": [
"status"
],
"type": "tag"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "http_reqs",
"orderByTime": "ASC",
"policy": "default",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "count"
}
]
],
"tags": []
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Requests",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:671",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:672",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "myinfluxdb",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 24,
"x": 0,
"y": 8
},
"hiddenSeries": false,
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.3.6",
"pointradius": 1,
"points": true,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": true,
"targets": [
{
"groupBy": [
{
"params": [
"1s"
],
"type": "time"
}
],
"measurement": "http_req_duration",
"orderByTime": "ASC",
"policy": "default",
"query": "SELECT mean(\"value\") FROM \"http_req_duration\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
"rawQuery": false,
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "http-req duration",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:543",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:544",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "myinfluxdb",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"fill": 3,
"fillGradient": 0,
"gridPos": {
"h": 9,
"w": 24,
"x": 0,
"y": 15
},
"hiddenSeries": false,
"id": 3,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.3.6",
"pointradius": 1,
"points": true,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"groupBy": [
{
"params": [
"1s"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "vus",
"orderByTime": "ASC",
"policy": "default",
"query": "SELECT mean(\"value\") FROM \"http_req_duration\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
"rawQuery": false,
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "vus",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:309",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:310",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": false,
"schemaVersion": 26,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "k6 performance test",
"uid": "Le2Ku9NMk",
"version": 1
}
+10
View File
@@ -0,0 +1,10 @@
apiVersion: 1
datasources:
- name: myinfluxdb
type: influxdb
access: proxy
database: k6
orgId: 1
url: http://influxdb:8086
isDefault: true
+73
View File
@@ -0,0 +1,73 @@
import http from 'k6/http';
import { sleep } from 'k6';
export let options = {
scenarios: {
normal: {
executor: 'constant-vus',
vus: 200,
duration: '10s',
exec: "normal",
gracefulStop: '0s',
},
notFound: {
executor: 'constant-vus',
vus: 200,
duration: '10s',
exec: "notFound",
gracefulStop: '0s',
startTime: '10s'
},
error: {
executor: 'constant-vus',
vus: 200,
duration: '10s',
exec: "error",
gracefulStop: '0s',
startTime: '20s'
},
slow: {
executor: 'constant-vus',
vus: 200,
duration: '10s',
exec: "slow",
gracefulStop: '10s',
startTime: '30s'
},
verySlow: {
executor: 'constant-vus',
vus: 200,
duration: '10s',
exec: "verySlow",
gracefulStop: '30s',
startTime: '50s'
}
}
};
const host = "http://host.docker.internal:5000";
export function normal() {
http.get(host + "/product/1/similar");
sleep(0.5);
}
export function slow() {
http.get(host + "/product/2/similar");
sleep(0.5);
}
export function verySlow() {
http.get(host + "/product/3/similar");
sleep(0.5);
}
export function notFound() {
http.get(host + "/product/4/similar");
sleep(0.5);
}
export function error() {
http.get(host + "/product/5/similar");
sleep(0.5);
}
+77
View File
@@ -0,0 +1,77 @@
[
{
"path": "/product/1/similarids",
"body": "[2,3,4]",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/2/similarids",
"body": "[3,100,1000]",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/3/similarids",
"body": "[100,1000,10000]",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/4/similarids",
"body": "[1,2,5]",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/5/similarids",
"body": "[1,2,6]",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/1",
"body": "{\"id\":\"1\",\"name\":\"Shirt\",\"price\":9.99,\"availability\":true}",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/2",
"body": "{\"id\":\"2\",\"name\":\"Dress\",\"price\":19.99,\"availability\":true}",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/3",
"body": "{\"id\":\"3\",\"name\":\"Blazer\",\"price\":29.99,\"availability\":false}",
"headers": {"Content-Type": "application/json"},
"delay": 100
},
{
"path": "/product/4",
"body": "{\"id\":\"4\",\"name\":\"Boots\",\"price\":39.99,\"availability\":true}",
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/5",
"body": "{\"message\":\"Product not found\"}",
"status": 404,
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/6",
"status": 500,
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/100",
"body": "{\"id\":\"100\",\"name\":\"Trousers\",\"price\":49.99,\"availability\":false}",
"delay": 1000,
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/1000",
"body": "{\"id\":\"1000\",\"name\":\"Coat\",\"price\":89.99,\"availability\":true}",
"delay": 5000,
"headers": {"Content-Type": "application/json"}
},
{
"path": "/product/10000",
"body": "{\"id\":\"10000\",\"name\":\"Leather jacket\",\"price\":89.99,\"availability\":true}",
"delay": 50000,
"headers": {"Content-Type": "application/json"}
}
]
+45
View File
@@ -0,0 +1,45 @@
openapi: 3.0.0
info:
title: SimilarProducts
version: '1.0'
servers:
- url: 'http://localhost:5000'
paths:
'/product/{productId}/similar':
parameters:
- schema:
type: string
name: productId
in: path
required: true
get:
operationId: get-product-similar
summary: Similar products
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
description: 'List of similar products to a given one ordered by similarity'
minItems: 0
uniqueItems: true
items:
type: object
required:
- id
- name
- price
- availability
properties:
id:
type: string
minLength: 1
name:
type: string
minLength: 1
price:
type: number
availability:
type: boolean