├── docs
└── testtask.pdf
├── cmd
└── umbrella-test-task
│ └── main.go
├── internal
├── app
│ ├── service
│ │ └── service.go
│ ├── mw
│ │ └── mw.go
│ └── endpoint
│ │ └── endpoint.go
└── pkg
│ └── app
│ └── app.go
├── go.mod
├── README.md
└── go.sum
/docs/testtask.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatecon/echo-middleware-assessment/HEAD/docs/testtask.pdf
--------------------------------------------------------------------------------
/cmd/umbrella-test-task/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/spatecon/echo-middleware-assessment/internal/pkg/app"
7 | )
8 |
9 | func main() {
10 | a, err := app.New()
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 |
15 | err = a.Run()
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internal/app/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type Service struct {
8 | }
9 |
10 | func New() *Service {
11 | return &Service{}
12 | }
13 |
14 | func (s *Service) DaysLeft() int64 {
15 | d := time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC)
16 |
17 | dur := time.Until(d)
18 |
19 | return int64(dur.Hours()) / 24
20 | }
21 |
--------------------------------------------------------------------------------
/internal/app/mw/mw.go:
--------------------------------------------------------------------------------
1 | package mw
2 |
3 | import (
4 | "log"
5 | "strings"
6 |
7 | "github.com/labstack/echo/v4"
8 | )
9 |
10 | const roleAdmin = "admin"
11 |
12 | func RoleCheck(next echo.HandlerFunc) echo.HandlerFunc {
13 | return func(ctx echo.Context) error {
14 | val := ctx.Request().Header.Get("User-Role")
15 | val = strings.ToLower(val)
16 |
17 | if strings.Contains(val, roleAdmin) {
18 | log.Println("red button user detected")
19 | }
20 |
21 | err := next(ctx)
22 | if err != nil {
23 | return err
24 | }
25 |
26 | return nil
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/app/endpoint/endpoint.go:
--------------------------------------------------------------------------------
1 | package endpoint
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | "github.com/labstack/echo/v4"
8 | )
9 |
10 | type Service interface {
11 | DaysLeft() int64
12 | }
13 |
14 | type Endpoint struct {
15 | s Service
16 | }
17 |
18 | func New(s Service) *Endpoint {
19 | return &Endpoint{
20 | s: s,
21 | }
22 | }
23 |
24 | func (e *Endpoint) Status(ctx echo.Context) error {
25 | d := e.s.DaysLeft()
26 |
27 | s := fmt.Sprintf("Days left: %d", d)
28 |
29 | err := ctx.String(http.StatusOK, s)
30 | if err != nil {
31 | return err
32 | }
33 |
34 | return nil
35 | }
36 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/spatecon/echo-middleware-assessment
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/labstack/echo/v4 v4.9.1 // indirect
7 | github.com/labstack/gommon v0.4.0 // indirect
8 | github.com/mattn/go-colorable v0.1.11 // indirect
9 | github.com/mattn/go-isatty v0.0.14 // indirect
10 | github.com/valyala/bytebufferpool v1.0.0 // indirect
11 | github.com/valyala/fasttemplate v1.2.1 // indirect
12 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
13 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
14 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
15 | golang.org/x/text v0.3.7 // indirect
16 | )
17 |
--------------------------------------------------------------------------------
/internal/pkg/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/labstack/echo/v4"
7 |
8 | "github.com/spatecon/echo-middleware-assessment/internal/app/endpoint"
9 | "github.com/spatecon/echo-middleware-assessment/internal/app/mw"
10 | "github.com/spatecon/echo-middleware-assessment/internal/app/service"
11 | )
12 |
13 | type App struct {
14 | e *endpoint.Endpoint
15 | s *service.Service
16 | echo *echo.Echo
17 | }
18 |
19 | func New() (*App, error) {
20 | a := &App{}
21 |
22 | a.s = service.New()
23 |
24 | a.e = endpoint.New(a.s)
25 |
26 | a.echo = echo.New()
27 |
28 | a.echo.Use(mw.RoleCheck)
29 |
30 | a.echo.GET("/status", a.e.Status)
31 |
32 | return a, nil
33 | }
34 |
35 | func (a *App) Run() error {
36 | fmt.Println("server running")
37 |
38 | err := a.echo.Start(":8080")
39 | if err != nil {
40 | return fmt.Errorf("failed to start http server: %w", err)
41 | }
42 |
43 | return nil
44 | }
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build echo middleware
2 |
3 | Example project of test assessment from imaginary company.
4 |
5 | - Full text of the task can be found [here](docs/testtask.pdf)
6 | - Video [here](https://youtu.be/Lsh3ylmXdJ8)
7 |
8 | ## Task
9 |
10 | You have **1 hour** to solve the task. If you are lucky enough and have time left, please try to optimise your solution.
11 |
12 | ### Build a middleware using echo framework
13 |
14 | First of all, you should create a handler which sends how many days left until 1 Jan 2025 and response with HTTP 200 OK status code.
15 |
16 | Secondly, build a middleware, which checks HTTP header `User-Role` presents and contains `admin` and prints `red button user detected` to the console (using default log package or any 3rd party) if so.
17 |
18 | ## Run
19 |
20 | ```shell
21 | go run github.com/spatecon/echo-middleware-assessment/cmd/umbrella-test-task
22 | ```
23 |
24 | ## Test
25 |
26 | ```shell
27 | curl --location --request GET '127.0.0.1:8080/status' \
28 | --header 'User-Role: admin'
29 | ```
30 |
31 | ## License
32 |
33 | You can share the source code or use it in learning purposes.
34 |
35 | ## Author
36 |
37 | Ilia Pavliukov
38 | i [at] ela.sh
39 | 2022
40 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
4 | github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
5 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
6 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
7 | github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
8 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
9 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
10 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
13 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
14 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
15 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
16 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
17 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
18 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
19 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
20 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
21 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
22 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
24 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
25 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
26 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
27 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
29 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
30 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
31 |
--------------------------------------------------------------------------------