hugdubois / svc-fizzbuzz

A simple fizzbuzz microservice

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Simple fizzbuzz microservice

made-with-Go GitHub go.mod Go version of a Go module GoReportCard example PkgGoDev

Build Status codecov

License: MIT

Table of contents

General info

The original fizzbuzz consists in writing all numbers from 1 to 100, and just replacing all multiples of 3 by "fizz", all multiples of 5 by "buzz", and all multiples of 15 by "fizzbuzz". The output would look like this:

"1,2,fizz,4,buzz,fizz,7,8,fizz,buzz,11,fizz,13,14,fizzbuzz,16,..."

The goal is to implement a web server that will expose a REST API endpoint that:

  • Accepts five parameters : three integers int1, int2 and limit, and two strings str1 and str2.
  • Returns a list of strings with numbers from 1 to limit, where: all multiples of int1 are replaced by str1, all multiples of int2 are replaced by str2, all multiples of int1 and int2 are replaced by str1str2.

The server needs to be:

  • Ready for production
  • Easy to maintain by other developers
  • Add a statistics endpoint allowing users to know what the most frequent request has been. This endpoint should:
    • Accept no parameter
    • Return the parameters corresponding to the most used request, as well as the number of hits for this request

The svc-fizzbuzz microservice exposes a REST API with the following endpoints:

  • /

    • Forwards to /version endpoint.
  • /version

    • Returns the service version.
  • /status

    • Returns the service status.
  • /metrics

    • Returns the prometheus metrics.
  • /swagger.json

    • Returns the swagger service description file.
  • __ api v1 fizzbuzz__

    • Returns a list of strings with numbers from 1 to limit, where: all multiples of int1 are replaced by str1, all multiples of int2 are replaced by str2, all multiples of int1 and int2 are replaced by str1str2.
    • Query String (or POST body parameters):
      • linit (positive integer) max value 100.000
      • int1 (positive integer) default value 3
      • int2 (positive integer) default value 5
      • str1 (string) default value fizz
      • str2 (string) default value buzz
  • /api/v1/fizzbuzz/top

    • Returns usage statistics of the /api/v1/fizzbuzz endpoint. It allows the users to know what the number of hits of that endpoint. And returns the parameters corresponding to it.

Technologies

Project is created with:

  • go: 1.16
  • redis: 6.0.9
  • docker: 20.10.6

Up and Running

⚠️ To baremetal and docker methods below, a redis-server is needed. To set the connection use --database-connect flag see Usage for more information.

Baremetal

Install localy (baremetal) (needs a redis server) :

$ go get github.com/hugdubois/svc-fizzbuzz

Docker

Install via Docker (needs a redis server) :

$ docker pull hugdubois/svc-fizzbuzz:1.0.0
$ docker run -d --name=svc-fizzbuzz --net=host -it hugdubois/svc-fizzbuzz:1.0.0 serve --database-connect localhost:6379

NOTE : docker images can be found on dockerhub.

Docker compose

Install via docker-compose (without git clone) (needs curl) :

$ curl https://raw.githubusercontent.com/hugdubois/svc-fizzbuzz/master/hack/remote-docker-compose.sh | sh

NOTE : that script will create a svc-fizzbuzz-compose with all required files

With git clone simply do :

$ git clone https://github.com/hugdubois/svc-fizzbuzz
$ cd svc-fizzbuzz
$ make compose-up

Some services are exposed:

Kubernetes

Install via kubernetes (needs kubectl):

$ kubectl apply -f https://raw.githubusercontent.com/hugdubois/svc-fizzbuzz/master/k8s-deployment.yaml

NOTE: if you use minikube do minikube service svc-fizzbuzz to expose and get the service ip or simply use kubectl port-forward deployment/svc-fizzbuzz 8080:8080.

Usage

svc-fizzbuzz is a sipmle fizzbuzz microservice.

Basic usage:

$ svc-fizzbuzz [command]

Available Commands:

  • completion generates the autocompletion script for the specified shell
  • help help about any command
  • serve launches the svc-fizzbuzz service webserver
  • version returns service version

To get help simply run svc-fizzbuzz help.

To launch the API webserver run: svc-fizzbuzz serve

  • Some flags are available :

    • --address (string) (short -a)
      • Must be used to set the HTTP server address.
      • ex: 127.0.0.1:13000
      • default: :8080
    • --cors-origin (string) (short -c)
      • Must be used to set the Cross Origin Resource Sharing AllowedOrigins. It's a string separed by |.
      • ex: http://*domain1.com|http://*domain2.com
      • default: *
    • --database-connect (string)
      • Must be used to set the redis server connection informations. [[db:]password@]host:port.
      • ex: 1:passW0rd@redis-server:6379
      • default: localhost:6379
    • --debug (boolean)
      • Must be used to force debug mode.
      • ex: 1:passW0rd@redis-server:6379
      • default: localhost:6379
    • --help (string)
      • Must be used to get help.
    • --read-timeout (duration)
      • Must be used to set the server read timeout (5s,5m,5h) before connection is cancelled.
      • ex: 10s
      • default: 5s
    • --shutdown-timeout (duration)
      • Must be used to set the server shutdown timeout (5s,5m,5h) graceful shutdown.
      • ex: 15s
      • default: 10s
    • --write-timeout (duration)
      • Must be used to set the server write timeout (5s,5m,5h) before connection is cancelled.
      • ex: 15s
      • default: 10s
  • The svc-fizzbuzz microservice exposes a REST API with the following endpoints:

    • /
      • Forwards to /version endpoint.
    • /version
      • Returns the service version.
    • /status
      • Returns the service status.
    • /metrics
      • Returns the prometheus metrics.
    • /swagger.json
      • Returns the swagger service description.
    • /api/v1/fizzbuzz
      • Returns a list of strings with numbers from 1 to limit, where: all multiples of int1 are replaced by str1, all multiples of int2 are replaced by str2, all multiples of int1 and int2 are replaced by str1str2.
      • Query String (or POST body parameters):
        • linit (positive integer) max value 100.000
        • int1 (positive integer) default value 3
        • int2 (positive integer) default value 5
        • str1 (string) default value fizz
        • str2 (string) default value buzz
    • /api/v1/fizzbuzz/top
      • Returns usage statistics of the /api/v1/fizzbuzz endpoint. It allows the users to know what the number of hits of that endpoint. And returns the parameters corresponding to it.

Examples

/

$ curl "localhost:8080/"

Should return

{"name":"svc-fizzbuzz","version":"v1.0.0"}

/status

$ curl "localhost:8080/status"

Should return

{"svc-alive":true,"store-alive":true}

/api/v1/fizzbuzz

This is the core API endpoint. This endpoint returns a list of strings with numbers from 1 to limit, where: all multiples of int1 are replaced by str1, all multiples of int2 are replaced by str2, all multiples of int1 and int2 are replaced by str1str2.

$ curl "localhost:8080/api/v1/fizzbuzz"

Should return a original fizzbuzz

{"fizzbuzz":["1","2","fizz","4","buzz","fizz","7","8","fizz","buzz","11","fizz","13","14","fizzbuzz","16","17","fizz","19","buzz","fizz","22","23","fizz","buzz","26","fizz","28","29","fizzbuzz","31","32","fizz","34","buzz","fizz","37","38","fizz","buzz","41","fizz","43","44","fizzbuzz","46","47","fizz","49","buzz","fizz","52","53","fizz","buzz","56","fizz","58","59","fizzbuzz","61","62","fizz","64","buzz","fizz","67","68","fizz","buzz","71","fizz","73","74","fizzbuzz","76","77","fizz","79","buzz","fizz","82","83","fizz","buzz","86","fizz","88","89","fizzbuzz","91","92","fizz","94","buzz","fizz","97","98","fizz","buzz"]}

The query string (or POST body parameters):

  • linit (positive integer) max value 100 000
  • int1 (positive integer) default value 3
  • int2 (positive integer) default value 5
  • str1 (string) default value fizz
  • str2 (string) default value buzz

⚠️ All of them are optional, if missing the default value is considered.

$ curl "localhost:8080/api/v1/fizzbuzz?limit=10"

Should return only ten values of the original fizzbuzz.

{"fizzbuzz":["1","2","fizz","4","buzz","fizz","7","8","fizz","buzz"]}

More complex query:

$ curl "localhost:8080/api/v1/fizzbuzz?limit=10&int1=2&int3=4&str1=bon&str2=coin"

Should return only ten values of a custom fizzbuzz.

{"fizzbuzz":["1","bon","3","bon","coin","bon","7","bon","9","boncoin"]}

⚠️ There is no restriction on the HTTP verb so POST, PUT, PATCH methods are accepted and valid. It is the same to all endpoints.

So all of these calls

$ curl -XPOST "localhost:8080/api/v1/fizzbuzz?limit=10&int1=2&int3=4&str1=bon&str2=coin"
$ curl -XPUT "localhost:8080/api/v1/fizzbuzz?limit=10&int1=2&int3=4&str1=bon&str2=coin"
$ curl -XPATCH "localhost:8080/api/v1/fizzbuzz?limit=10&int1=2&int3=4&str1=bon&str2=coin"
$ curl -XPOST -d "limit=10&int1=2&int3=4&str1=bon&str2=coin" "localhost:8080/api/v1/fizzbuzz"
$ curl -XPUT -d "limit=10&int1=2&int3=4&str1=bon&str2=coin" "localhost:8080/api/v1/fizzbuzz"
$ curl -XPATCH -d "limit=10&int1=2&int3=4&str1=bon&str2=coin" "localhost:8080/api/v1/fizzbuzz"

Should return only ten values of a custom fizzbuzz.

{"fizzbuzz":["1","bon","3","bon","coin","bon","7","bon","9","boncoin"]}

⚠️ If bad parameters are send, an error is returned with the 422 Unprocessable Entity with the reason of the error.

So :

$ curl -v "localhost:8080/api/v1/fizzbuzz?limit=infini"

Should return an error.

...
< HTTP/1.1 422 Unprocessable Entity
...
{"code":422,"message":"Bad parameter: 'limit' must be a positive number - got (infini)"}%

And

$ curl "localhost:8080/api/v1/fizzbuzz?int1=bon"

Should return also an error.

{"code":422,"message":"Bad parameter: 'int1' must be a positive number - got (bon)"}%

⚠️ Only one error is returned

So :

curl "localhost:8080/api/v1/fizzbuzz?int1=bad&int2=bad"

Should return only one error.

{"code":422,"message":"Bad parameter: 'int1' must be a positive number - got (bad)"}%

/api/v1/fizzbuzz/top

This endpoint returns the usage statistics of the /api/v1/fizzbuzz endpoint. It allows the users to know what the number of hits of that endpoint. And returns the parameters corresponding to it.

$ curl "localhost:8080/api/v1/fizzbuzz/top"

Should return only one error.

{"data":{"params":{"limit":10,"int1":2,"str1":"bon","int2":5,"str2":"coin"},"count_request":10}}%

/metrics

This endpoint exposes the prometheus metrics.

$ curl "localhost:8080/metrics"

Should return.

...
# HELP fizzbuzz_http_requests_duration_millisecond How long it took to process the request, partitioned by status code, method and HTTP path.
# TYPE fizzbuzz_http_requests_duration_millisecond summary
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="GET",path="/"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="GET",path="/"} 1
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="GET",path="/api/v1/fizzbuzz"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="GET",path="/api/v1/fizzbuzz"} 3
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="GET",path="/api/v1/fizzbuzz/top"} 1
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="GET",path="/api/v1/fizzbuzz/top"} 1
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="GET",path="/status"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="GET",path="/status"} 1
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="HEAD",path="/api/v1/fizzbuzz"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="HEAD",path="/api/v1/fizzbuzz"} 1
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="OPTIONS",path="/api/v1/fizzbuzz"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="OPTIONS",path="/api/v1/fizzbuzz"} 1
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="PATCH",path="/api/v1/fizzbuzz"} 4
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="PATCH",path="/api/v1/fizzbuzz"} 2
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="POST",path="/api/v1/fizzbuzz"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="POST",path="/api/v1/fizzbuzz"} 2
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="POST",path="/api/v1/fizzbuzz/top"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="POST",path="/api/v1/fizzbuzz/top"} 1
fizzbuzz_http_requests_duration_millisecond_sum{code="OK",method="PUT",path="/api/v1/fizzbuzz"} 4
fizzbuzz_http_requests_duration_millisecond_count{code="OK",method="PUT",path="/api/v1/fizzbuzz"} 5
fizzbuzz_http_requests_duration_millisecond_sum{code="Unprocessable Entity",method="GET",path="/api/v1/fizzbuzz"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="Unprocessable Entity",method="GET",path="/api/v1/fizzbuzz"} 6
fizzbuzz_http_requests_duration_millisecond_sum{code="Unprocessable Entity",method="PATCH",path="/api/v1/fizzbuzz"} 0
fizzbuzz_http_requests_duration_millisecond_count{code="Unprocessable Entity",method="PATCH",path="/api/v1/fizzbuzz"} 1
...

/swagger.json

This endpoint exposes the swagger service description.

$ curl "localhost:8080/metrics"

Should return.

{
    ...
    "swagger": "2.0",
    ...
}

Not found

All others calls return an 404 Not Found error.

$ curl -v "localhost:8080/does_not_exists"

Should return.

...
< HTTP/1.1 404 Not Found
...
{"code":404,"message":"Not Found"}

Contribute

This repository follows the Conventional Commits and standard git flow.

See package documentation on pkg.go.dev.

Code coverage can be found on codecov.

CI is on travis and github actions.

Make directives

  • make build - (default) build the service and inject the version (-ldflags).
  • make version - Display current version (VERSION file).
  • make tools - Build tools that are in _tools directory.
  • make test - Run test.
  • make test-v - Run test on verbose mode.
  • make test-live - Run test on infinite shell loop.
  • make test-cover - Run test with coverage.
  • make test-cover-profile - Run test with coverage and generate a profile coverage file.
  • make test-cover-report - Run test with coverage UI on a browser (go tool cover -html=...).
  • make test-cover-func - Run test with total coverage computation (go tool cover -func=...).
  • make gen-swagger - Generate swagger service declaration from project annotation.
  • make serve - Build and launch the server API with the debug mode activate.
  • make clean - Removing all generated files (compiled files, code coverage, ...).
  • make docker-tag - Generate .env file to docker-compose.
  • make docker - Generate docker image.
  • make docker-push - Push the docker image to the repository (use DOCKER_REGISTRY and DOCKER_IMAGE_NAME like this DOCKER_REGISTRY={{hostname}}:{{port}} make docker-push)
  • make docker-run - Run service with docker.
  • make docker-rm - Remove service from docker.
  • make compose-up - Run docker-compose up -d.
  • make compose-down - Run docker-compose up.
  • make compose-ps - Run docker-compose ps.
  • make k8s-deploy - Deploy kubernetes deployment on the current cluster via kubectl.
  • make k8s-delete - Delete kubernetes deployment from the current cluster via kubectl.
  • make update-pkg-cache - Performs a go get via https://proxy.golang.org as GOPROXY.

Directories structure

  • _tools - The directory contains some used tools (cf. make tools)
  • cmd - The directory contains the cli commands.
  • core - The directory is the core domain layer.
  • docs - The directory is dedicated to swagger it contains generated files by swag tool.
  • hack - The directory contains some shell scripts.
  • helpers - The directory contains a helpers package.
  • infra - The directory contains some infrastructure code to docker-compose.
  • middlewares - The directory contains all HTTP middlewares.
  • service - The directory is the service layer.
  • store - The directory is persistence layer.
  • vendor - The directory is go mod vendoring.

Notes

The statistics to /api/v1/fizzbuzz/top endpoint are stored in a redis sorted sets.

To retrieve the most used request a simple ZREVRANGE k 0 0 WITHSCORES is good enough.

Roadmap

  • separate core domain (without dependencies)
  • nice cli with usage and help
  • light simple http service (with graceful shutdown and errors recovering)
  • nice requests log
  • allows CORS
  • endpoint to expose prometheus metrics
  • CI
  • test coverage >~ 80% (core 100%)
  • docker / docker-compose
  • simple k8s deployment
    • use kustomize to bump TAG in k8s-deployment.yaml
  • add swagger file and expose it on /swagger.json endpoint
  • add a cache
  • TLS support

Authors

Hugues Dubois

Support

mail: hugdubois@gmail.com

License

MIT

About

A simple fizzbuzz microservice

License:MIT License


Languages

Language:Go 94.5%Language:Makefile 4.2%Language:Dockerfile 0.8%Language:Shell 0.5%