From 0e3e9d80eb14639bc42935795f7ca3b73da36304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Sm=C3=B3=C5=82ka?= Date: Thu, 28 Jan 2021 10:41:03 +0100 Subject: [PATCH] Introduce test suite --- .e2e.env | 5 + .env | 2 +- .test.env | 8 +- Makefile | 23 +- docker-compose.yml | 8 + .../client/trainer/openapi_client_gen.go | 565 +++++++++ .../client/trainer/openapi_types.gen.go | 53 + .../client/trainings/openapi_client_gen.go | 1015 +++++++++++++++++ .../client/trainings/openapi_types.gen.go | 56 + .../common/client/users/openapi_client_gen.go | 221 ++++ .../common/client/users/openapi_types.gen.go | 11 + internal/common/go.mod | 3 + internal/common/go.sum | 43 + internal/common/server/grpc.go | 9 +- internal/common/server/http.go | 6 +- internal/common/tests/clients.go | 153 +++ internal/common/tests/e2e_test.go | 69 ++ internal/common/tests/hours.go | 15 + internal/common/tests/jwt.go | 35 + internal/common/tests/wait.go | 33 + .../trainer/adapters/hour_mysql_repository.go | 2 +- internal/trainer/go.mod | 5 +- internal/trainer/go.sum | 26 +- internal/trainer/main.go | 45 +- internal/trainer/service/application.go | 48 + internal/trainer/service/component_test.go | 108 ++ .../trainings_firestore_repository_test.go | 19 +- internal/trainings/go.mod | 8 +- internal/trainings/go.sum | 29 +- internal/trainings/main.go | 49 +- internal/trainings/service/component_test.go | 80 ++ internal/trainings/service/mocks.go | 28 + internal/trainings/service/service.go | 61 + internal/users/go.mod | 1 - internal/users/go.sum | 29 +- internal/users/openapi_types.gen.go | 6 - scripts/test.sh | 8 + 37 files changed, 2740 insertions(+), 145 deletions(-) create mode 100644 .e2e.env create mode 100644 internal/common/client/trainer/openapi_client_gen.go create mode 100644 internal/common/client/trainer/openapi_types.gen.go create mode 100644 internal/common/client/trainings/openapi_client_gen.go create mode 100644 internal/common/client/trainings/openapi_types.gen.go create mode 100644 internal/common/client/users/openapi_client_gen.go create mode 100644 internal/common/client/users/openapi_types.gen.go create mode 100644 internal/common/tests/clients.go create mode 100644 internal/common/tests/e2e_test.go create mode 100644 internal/common/tests/hours.go create mode 100644 internal/common/tests/jwt.go create mode 100644 internal/common/tests/wait.go create mode 100644 internal/trainer/service/application.go create mode 100644 internal/trainer/service/component_test.go create mode 100644 internal/trainings/service/component_test.go create mode 100644 internal/trainings/service/mocks.go create mode 100644 internal/trainings/service/service.go create mode 100755 scripts/test.sh diff --git a/.e2e.env b/.e2e.env new file mode 100644 index 0000000..33bf275 --- /dev/null +++ b/.e2e.env @@ -0,0 +1,5 @@ +TRAINER_HTTP_ADDR=localhost:3000 +TRAINER_GRPC_ADDR=localhost:3010 +TRAININGS_HTTP_ADDR=localhost:3001 +USERS_GRPC_ADDR=localhost:3020 +USERS_HTTP_ADDR=localhost:3002 diff --git a/.env b/.env index fc18871..dfb8746 100644 --- a/.env +++ b/.env @@ -21,4 +21,4 @@ MYSQL_ADDR=localhost MYSQL_DATABASE=db MYSQL_USER=user MYSQL_PASSWORD=password -MYSQL_RANDOM_ROOT_PASSWORD=true \ No newline at end of file +MYSQL_RANDOM_ROOT_PASSWORD=true diff --git a/.test.env b/.test.env index dcae954..d81114f 100644 --- a/.test.env +++ b/.test.env @@ -1,2 +1,8 @@ -FIRESTORE_EMULATOR_HOST=localhost:8787 +FIRESTORE_EMULATOR_HOST=localhost:8788 MYSQL_ADDR=localhost + +TRAINER_HTTP_ADDR=localhost:5000 +TRAINER_GRPC_ADDR=localhost:5010 +TRAININGS_HTTP_ADDR=localhost:5001 +USERS_HTTP_ADDR=localhost:5002 +USERS_GRPC_ADDR=localhost:5020 diff --git a/Makefile b/Makefile index 9a1ab72..5e2482f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ include .env -include .test.env export .PHONY: openapi @@ -9,12 +8,18 @@ openapi: openapi_http openapi_js openapi_http: oapi-codegen -generate types -o internal/trainings/ports/openapi_types.gen.go -package ports api/openapi/trainings.yml oapi-codegen -generate chi-server -o internal/trainings/ports/openapi_api.gen.go -package ports api/openapi/trainings.yml + oapi-codegen -generate types -o internal/common/client/trainings/openapi_types.gen.go -package trainings api/openapi/trainings.yml + oapi-codegen -generate client -o internal/common/client/trainings/openapi_client_gen.go -package trainings api/openapi/trainings.yml oapi-codegen -generate types -o internal/trainer/ports/openapi_types.gen.go -package ports api/openapi/trainer.yml oapi-codegen -generate chi-server -o internal/trainer/ports/openapi_api.gen.go -package ports api/openapi/trainer.yml + oapi-codegen -generate types -o internal/common/client/trainer/openapi_types.gen.go -package trainer api/openapi/trainer.yml + oapi-codegen -generate client -o internal/common/client/trainer/openapi_client_gen.go -package trainer api/openapi/trainer.yml oapi-codegen -generate types -o internal/users/openapi_types.gen.go -package main api/openapi/users.yml oapi-codegen -generate chi-server -o internal/users/openapi_api.gen.go -package main api/openapi/users.yml + oapi-codegen -generate types -o internal/common/client/users/openapi_types.gen.go -package users api/openapi/users.yml + oapi-codegen -generate client -o internal/common/client/users/openapi_client_gen.go -package users api/openapi/users.yml .PHONY: openapi_js openapi_js: @@ -52,14 +57,8 @@ fmt: mycli: mycli -u ${MYSQL_USER} -p ${MYSQL_PASSWORD} ${MYSQL_DATABASE} -INERNAL_PACKAGES := $(wildcard internal/*) - -ifeq (test,$(firstword $(MAKECMDGOALS))) - TEST_ARGS := $(subst $$,$$$$,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))) - $(eval $(TEST_ARGS):;@:) -endif - -.PHONY: test $(INERNAL_PACKAGES) -test: $(INERNAL_PACKAGES) -$(INERNAL_PACKAGES): - @(cd $@ && go test -count=1 -race ./... $(subst $$$$,$$,$(TEST_ARGS))) +test: + @./scripts/test.sh common .e2e.env + @./scripts/test.sh trainer .test.env + @./scripts/test.sh trainings .test.env + @./scripts/test.sh users .test.env diff --git a/docker-compose.yml b/docker-compose.yml index 2401c47..cb19149 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -98,6 +98,14 @@ services: - "127.0.0.1:4000:4000" restart: unless-stopped + firestore-component-tests: + image: karhoo/firestore-emulator:0.3.2 + env_file: + - .env + ports: + - "127.0.0.1:8788:8787" + restart: unless-stopped + mysql: image: mysql:8 env_file: diff --git a/internal/common/client/trainer/openapi_client_gen.go b/internal/common/client/trainer/openapi_client_gen.go new file mode 100644 index 0000000..cc31af1 --- /dev/null +++ b/internal/common/client/trainer/openapi_client_gen.go @@ -0,0 +1,565 @@ +// Package trainer provides primitives to interact the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. +package trainer + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/deepmap/oapi-codegen/pkg/runtime" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A callback for modifying requests which are generated before sending over + // the network. + RequestEditor RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = http.DefaultClient + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditor = fn + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetTrainerAvailableHours request + GetTrainerAvailableHours(ctx context.Context, params *GetTrainerAvailableHoursParams) (*http.Response, error) + + // MakeHourAvailable request with any body + MakeHourAvailableWithBody(ctx context.Context, contentType string, body io.Reader) (*http.Response, error) + + MakeHourAvailable(ctx context.Context, body MakeHourAvailableJSONRequestBody) (*http.Response, error) + + // MakeHourUnavailable request with any body + MakeHourUnavailableWithBody(ctx context.Context, contentType string, body io.Reader) (*http.Response, error) + + MakeHourUnavailable(ctx context.Context, body MakeHourUnavailableJSONRequestBody) (*http.Response, error) +} + +func (c *Client) GetTrainerAvailableHours(ctx context.Context, params *GetTrainerAvailableHoursParams) (*http.Response, error) { + req, err := NewGetTrainerAvailableHoursRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) MakeHourAvailableWithBody(ctx context.Context, contentType string, body io.Reader) (*http.Response, error) { + req, err := NewMakeHourAvailableRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) MakeHourAvailable(ctx context.Context, body MakeHourAvailableJSONRequestBody) (*http.Response, error) { + req, err := NewMakeHourAvailableRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) MakeHourUnavailableWithBody(ctx context.Context, contentType string, body io.Reader) (*http.Response, error) { + req, err := NewMakeHourUnavailableRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) MakeHourUnavailable(ctx context.Context, body MakeHourUnavailableJSONRequestBody) (*http.Response, error) { + req, err := NewMakeHourUnavailableRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +// NewGetTrainerAvailableHoursRequest generates requests for GetTrainerAvailableHours +func NewGetTrainerAvailableHoursRequest(server string, params *GetTrainerAvailableHoursParams) (*http.Request, error) { + var err error + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainer/calendar") + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + queryValues := queryUrl.Query() + + if queryFrag, err := runtime.StyleParam("form", true, "dateFrom", params.DateFrom); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + if queryFrag, err := runtime.StyleParam("form", true, "dateTo", params.DateTo); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + queryUrl.RawQuery = queryValues.Encode() + + req, err := http.NewRequest("GET", queryUrl.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewMakeHourAvailableRequest calls the generic MakeHourAvailable builder with application/json body +func NewMakeHourAvailableRequest(server string, body MakeHourAvailableJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewMakeHourAvailableRequestWithBody(server, "application/json", bodyReader) +} + +// NewMakeHourAvailableRequestWithBody generates requests for MakeHourAvailable with any type of body +func NewMakeHourAvailableRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainer/calendar/make-hour-available") + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryUrl.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + return req, nil +} + +// NewMakeHourUnavailableRequest calls the generic MakeHourUnavailable builder with application/json body +func NewMakeHourUnavailableRequest(server string, body MakeHourUnavailableJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewMakeHourUnavailableRequestWithBody(server, "application/json", bodyReader) +} + +// NewMakeHourUnavailableRequestWithBody generates requests for MakeHourUnavailable with any type of body +func NewMakeHourUnavailableRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainer/calendar/make-hour-unavailable") + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryUrl.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + return req, nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetTrainerAvailableHours request + GetTrainerAvailableHoursWithResponse(ctx context.Context, params *GetTrainerAvailableHoursParams) (*GetTrainerAvailableHoursResponse, error) + + // MakeHourAvailable request with any body + MakeHourAvailableWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader) (*MakeHourAvailableResponse, error) + + MakeHourAvailableWithResponse(ctx context.Context, body MakeHourAvailableJSONRequestBody) (*MakeHourAvailableResponse, error) + + // MakeHourUnavailable request with any body + MakeHourUnavailableWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader) (*MakeHourUnavailableResponse, error) + + MakeHourUnavailableWithResponse(ctx context.Context, body MakeHourUnavailableJSONRequestBody) (*MakeHourUnavailableResponse, error) +} + +type GetTrainerAvailableHoursResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]Date + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r GetTrainerAvailableHoursResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTrainerAvailableHoursResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type MakeHourAvailableResponse struct { + Body []byte + HTTPResponse *http.Response + JSON204 *[]Date + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r MakeHourAvailableResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MakeHourAvailableResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type MakeHourUnavailableResponse struct { + Body []byte + HTTPResponse *http.Response + JSON204 *[]Date + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r MakeHourUnavailableResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MakeHourUnavailableResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GetTrainerAvailableHoursWithResponse request returning *GetTrainerAvailableHoursResponse +func (c *ClientWithResponses) GetTrainerAvailableHoursWithResponse(ctx context.Context, params *GetTrainerAvailableHoursParams) (*GetTrainerAvailableHoursResponse, error) { + rsp, err := c.GetTrainerAvailableHours(ctx, params) + if err != nil { + return nil, err + } + return ParseGetTrainerAvailableHoursResponse(rsp) +} + +// MakeHourAvailableWithBodyWithResponse request with arbitrary body returning *MakeHourAvailableResponse +func (c *ClientWithResponses) MakeHourAvailableWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader) (*MakeHourAvailableResponse, error) { + rsp, err := c.MakeHourAvailableWithBody(ctx, contentType, body) + if err != nil { + return nil, err + } + return ParseMakeHourAvailableResponse(rsp) +} + +func (c *ClientWithResponses) MakeHourAvailableWithResponse(ctx context.Context, body MakeHourAvailableJSONRequestBody) (*MakeHourAvailableResponse, error) { + rsp, err := c.MakeHourAvailable(ctx, body) + if err != nil { + return nil, err + } + return ParseMakeHourAvailableResponse(rsp) +} + +// MakeHourUnavailableWithBodyWithResponse request with arbitrary body returning *MakeHourUnavailableResponse +func (c *ClientWithResponses) MakeHourUnavailableWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader) (*MakeHourUnavailableResponse, error) { + rsp, err := c.MakeHourUnavailableWithBody(ctx, contentType, body) + if err != nil { + return nil, err + } + return ParseMakeHourUnavailableResponse(rsp) +} + +func (c *ClientWithResponses) MakeHourUnavailableWithResponse(ctx context.Context, body MakeHourUnavailableJSONRequestBody) (*MakeHourUnavailableResponse, error) { + rsp, err := c.MakeHourUnavailable(ctx, body) + if err != nil { + return nil, err + } + return ParseMakeHourUnavailableResponse(rsp) +} + +// ParseGetTrainerAvailableHoursResponse parses an HTTP response from a GetTrainerAvailableHoursWithResponse call +func ParseGetTrainerAvailableHoursResponse(rsp *http.Response) (*GetTrainerAvailableHoursResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &GetTrainerAvailableHoursResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []Date + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseMakeHourAvailableResponse parses an HTTP response from a MakeHourAvailableWithResponse call +func ParseMakeHourAvailableResponse(rsp *http.Response) (*MakeHourAvailableResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &MakeHourAvailableResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 204: + var dest []Date + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON204 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseMakeHourUnavailableResponse parses an HTTP response from a MakeHourUnavailableWithResponse call +func ParseMakeHourUnavailableResponse(rsp *http.Response) (*MakeHourUnavailableResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &MakeHourUnavailableResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 204: + var dest []Date + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON204 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} diff --git a/internal/common/client/trainer/openapi_types.gen.go b/internal/common/client/trainer/openapi_types.gen.go new file mode 100644 index 0000000..44004b7 --- /dev/null +++ b/internal/common/client/trainer/openapi_types.gen.go @@ -0,0 +1,53 @@ +// Package trainer provides primitives to interact the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. +package trainer + +import ( + "time" + + openapi_types "github.com/deepmap/oapi-codegen/pkg/types" +) + +// Date defines model for Date. +type Date struct { + Date openapi_types.Date `json:"date"` + HasFreeHours bool `json:"hasFreeHours"` + Hours []Hour `json:"hours"` +} + +// Error defines model for Error. +type Error struct { + Message string `json:"message"` + Slug string `json:"slug"` +} + +// Hour defines model for Hour. +type Hour struct { + Available bool `json:"available"` + HasTrainingScheduled bool `json:"hasTrainingScheduled"` + Hour time.Time `json:"hour"` +} + +// HourUpdate defines model for HourUpdate. +type HourUpdate struct { + Hours []time.Time `json:"hours"` +} + +// GetTrainerAvailableHoursParams defines parameters for GetTrainerAvailableHours. +type GetTrainerAvailableHoursParams struct { + DateFrom time.Time `json:"dateFrom"` + DateTo time.Time `json:"dateTo"` +} + +// MakeHourAvailableJSONBody defines parameters for MakeHourAvailable. +type MakeHourAvailableJSONBody HourUpdate + +// MakeHourUnavailableJSONBody defines parameters for MakeHourUnavailable. +type MakeHourUnavailableJSONBody HourUpdate + +// MakeHourAvailableRequestBody defines body for MakeHourAvailable for application/json ContentType. +type MakeHourAvailableJSONRequestBody MakeHourAvailableJSONBody + +// MakeHourUnavailableRequestBody defines body for MakeHourUnavailable for application/json ContentType. +type MakeHourUnavailableJSONRequestBody MakeHourUnavailableJSONBody diff --git a/internal/common/client/trainings/openapi_client_gen.go b/internal/common/client/trainings/openapi_client_gen.go new file mode 100644 index 0000000..c42b49c --- /dev/null +++ b/internal/common/client/trainings/openapi_client_gen.go @@ -0,0 +1,1015 @@ +// Package trainings provides primitives to interact the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. +package trainings + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/deepmap/oapi-codegen/pkg/runtime" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A callback for modifying requests which are generated before sending over + // the network. + RequestEditor RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = http.DefaultClient + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditor = fn + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetTrainings request + GetTrainings(ctx context.Context) (*http.Response, error) + + // CreateTraining request with any body + CreateTrainingWithBody(ctx context.Context, contentType string, body io.Reader) (*http.Response, error) + + CreateTraining(ctx context.Context, body CreateTrainingJSONRequestBody) (*http.Response, error) + + // CancelTraining request + CancelTraining(ctx context.Context, trainingUUID string) (*http.Response, error) + + // ApproveRescheduleTraining request + ApproveRescheduleTraining(ctx context.Context, trainingUUID string) (*http.Response, error) + + // RejectRescheduleTraining request + RejectRescheduleTraining(ctx context.Context, trainingUUID string) (*http.Response, error) + + // RequestRescheduleTraining request with any body + RequestRescheduleTrainingWithBody(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*http.Response, error) + + RequestRescheduleTraining(ctx context.Context, trainingUUID string, body RequestRescheduleTrainingJSONRequestBody) (*http.Response, error) + + // RescheduleTraining request with any body + RescheduleTrainingWithBody(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*http.Response, error) + + RescheduleTraining(ctx context.Context, trainingUUID string, body RescheduleTrainingJSONRequestBody) (*http.Response, error) +} + +func (c *Client) GetTrainings(ctx context.Context) (*http.Response, error) { + req, err := NewGetTrainingsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) CreateTrainingWithBody(ctx context.Context, contentType string, body io.Reader) (*http.Response, error) { + req, err := NewCreateTrainingRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) CreateTraining(ctx context.Context, body CreateTrainingJSONRequestBody) (*http.Response, error) { + req, err := NewCreateTrainingRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) CancelTraining(ctx context.Context, trainingUUID string) (*http.Response, error) { + req, err := NewCancelTrainingRequest(c.Server, trainingUUID) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) ApproveRescheduleTraining(ctx context.Context, trainingUUID string) (*http.Response, error) { + req, err := NewApproveRescheduleTrainingRequest(c.Server, trainingUUID) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) RejectRescheduleTraining(ctx context.Context, trainingUUID string) (*http.Response, error) { + req, err := NewRejectRescheduleTrainingRequest(c.Server, trainingUUID) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) RequestRescheduleTrainingWithBody(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*http.Response, error) { + req, err := NewRequestRescheduleTrainingRequestWithBody(c.Server, trainingUUID, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) RequestRescheduleTraining(ctx context.Context, trainingUUID string, body RequestRescheduleTrainingJSONRequestBody) (*http.Response, error) { + req, err := NewRequestRescheduleTrainingRequest(c.Server, trainingUUID, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) RescheduleTrainingWithBody(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*http.Response, error) { + req, err := NewRescheduleTrainingRequestWithBody(c.Server, trainingUUID, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +func (c *Client) RescheduleTraining(ctx context.Context, trainingUUID string, body RescheduleTrainingJSONRequestBody) (*http.Response, error) { + req, err := NewRescheduleTrainingRequest(c.Server, trainingUUID, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +// NewGetTrainingsRequest generates requests for GetTrainings +func NewGetTrainingsRequest(server string) (*http.Request, error) { + var err error + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings") + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryUrl.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateTrainingRequest calls the generic CreateTraining builder with application/json body +func NewCreateTrainingRequest(server string, body CreateTrainingJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateTrainingRequestWithBody(server, "application/json", bodyReader) +} + +// NewCreateTrainingRequestWithBody generates requests for CreateTraining with any type of body +func NewCreateTrainingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings") + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryUrl.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + return req, nil +} + +// NewCancelTrainingRequest generates requests for CancelTraining +func NewCancelTrainingRequest(server string, trainingUUID string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParam("simple", false, "trainingUUID", trainingUUID) + if err != nil { + return nil, err + } + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings/%s", pathParam0) + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryUrl.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewApproveRescheduleTrainingRequest generates requests for ApproveRescheduleTraining +func NewApproveRescheduleTrainingRequest(server string, trainingUUID string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParam("simple", false, "trainingUUID", trainingUUID) + if err != nil { + return nil, err + } + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings/%s/approve-reschedule", pathParam0) + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryUrl.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRejectRescheduleTrainingRequest generates requests for RejectRescheduleTraining +func NewRejectRescheduleTrainingRequest(server string, trainingUUID string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParam("simple", false, "trainingUUID", trainingUUID) + if err != nil { + return nil, err + } + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings/%s/reject-reschedule", pathParam0) + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryUrl.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRequestRescheduleTrainingRequest calls the generic RequestRescheduleTraining builder with application/json body +func NewRequestRescheduleTrainingRequest(server string, trainingUUID string, body RequestRescheduleTrainingJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRequestRescheduleTrainingRequestWithBody(server, trainingUUID, "application/json", bodyReader) +} + +// NewRequestRescheduleTrainingRequestWithBody generates requests for RequestRescheduleTraining with any type of body +func NewRequestRescheduleTrainingRequestWithBody(server string, trainingUUID string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParam("simple", false, "trainingUUID", trainingUUID) + if err != nil { + return nil, err + } + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings/%s/request-reschedule", pathParam0) + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryUrl.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + return req, nil +} + +// NewRescheduleTrainingRequest calls the generic RescheduleTraining builder with application/json body +func NewRescheduleTrainingRequest(server string, trainingUUID string, body RescheduleTrainingJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRescheduleTrainingRequestWithBody(server, trainingUUID, "application/json", bodyReader) +} + +// NewRescheduleTrainingRequestWithBody generates requests for RescheduleTraining with any type of body +func NewRescheduleTrainingRequestWithBody(server string, trainingUUID string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParam("simple", false, "trainingUUID", trainingUUID) + if err != nil { + return nil, err + } + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/trainings/%s/reschedule", pathParam0) + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryUrl.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + return req, nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetTrainings request + GetTrainingsWithResponse(ctx context.Context) (*GetTrainingsResponse, error) + + // CreateTraining request with any body + CreateTrainingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader) (*CreateTrainingResponse, error) + + CreateTrainingWithResponse(ctx context.Context, body CreateTrainingJSONRequestBody) (*CreateTrainingResponse, error) + + // CancelTraining request + CancelTrainingWithResponse(ctx context.Context, trainingUUID string) (*CancelTrainingResponse, error) + + // ApproveRescheduleTraining request + ApproveRescheduleTrainingWithResponse(ctx context.Context, trainingUUID string) (*ApproveRescheduleTrainingResponse, error) + + // RejectRescheduleTraining request + RejectRescheduleTrainingWithResponse(ctx context.Context, trainingUUID string) (*RejectRescheduleTrainingResponse, error) + + // RequestRescheduleTraining request with any body + RequestRescheduleTrainingWithBodyWithResponse(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*RequestRescheduleTrainingResponse, error) + + RequestRescheduleTrainingWithResponse(ctx context.Context, trainingUUID string, body RequestRescheduleTrainingJSONRequestBody) (*RequestRescheduleTrainingResponse, error) + + // RescheduleTraining request with any body + RescheduleTrainingWithBodyWithResponse(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*RescheduleTrainingResponse, error) + + RescheduleTrainingWithResponse(ctx context.Context, trainingUUID string, body RescheduleTrainingJSONRequestBody) (*RescheduleTrainingResponse, error) +} + +type GetTrainingsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Trainings + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r GetTrainingsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTrainingsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CreateTrainingResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r CreateTrainingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateTrainingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CancelTrainingResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r CancelTrainingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CancelTrainingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type ApproveRescheduleTrainingResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r ApproveRescheduleTrainingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ApproveRescheduleTrainingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RejectRescheduleTrainingResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r RejectRescheduleTrainingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RejectRescheduleTrainingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RequestRescheduleTrainingResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r RequestRescheduleTrainingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RequestRescheduleTrainingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RescheduleTrainingResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r RescheduleTrainingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RescheduleTrainingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GetTrainingsWithResponse request returning *GetTrainingsResponse +func (c *ClientWithResponses) GetTrainingsWithResponse(ctx context.Context) (*GetTrainingsResponse, error) { + rsp, err := c.GetTrainings(ctx) + if err != nil { + return nil, err + } + return ParseGetTrainingsResponse(rsp) +} + +// CreateTrainingWithBodyWithResponse request with arbitrary body returning *CreateTrainingResponse +func (c *ClientWithResponses) CreateTrainingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader) (*CreateTrainingResponse, error) { + rsp, err := c.CreateTrainingWithBody(ctx, contentType, body) + if err != nil { + return nil, err + } + return ParseCreateTrainingResponse(rsp) +} + +func (c *ClientWithResponses) CreateTrainingWithResponse(ctx context.Context, body CreateTrainingJSONRequestBody) (*CreateTrainingResponse, error) { + rsp, err := c.CreateTraining(ctx, body) + if err != nil { + return nil, err + } + return ParseCreateTrainingResponse(rsp) +} + +// CancelTrainingWithResponse request returning *CancelTrainingResponse +func (c *ClientWithResponses) CancelTrainingWithResponse(ctx context.Context, trainingUUID string) (*CancelTrainingResponse, error) { + rsp, err := c.CancelTraining(ctx, trainingUUID) + if err != nil { + return nil, err + } + return ParseCancelTrainingResponse(rsp) +} + +// ApproveRescheduleTrainingWithResponse request returning *ApproveRescheduleTrainingResponse +func (c *ClientWithResponses) ApproveRescheduleTrainingWithResponse(ctx context.Context, trainingUUID string) (*ApproveRescheduleTrainingResponse, error) { + rsp, err := c.ApproveRescheduleTraining(ctx, trainingUUID) + if err != nil { + return nil, err + } + return ParseApproveRescheduleTrainingResponse(rsp) +} + +// RejectRescheduleTrainingWithResponse request returning *RejectRescheduleTrainingResponse +func (c *ClientWithResponses) RejectRescheduleTrainingWithResponse(ctx context.Context, trainingUUID string) (*RejectRescheduleTrainingResponse, error) { + rsp, err := c.RejectRescheduleTraining(ctx, trainingUUID) + if err != nil { + return nil, err + } + return ParseRejectRescheduleTrainingResponse(rsp) +} + +// RequestRescheduleTrainingWithBodyWithResponse request with arbitrary body returning *RequestRescheduleTrainingResponse +func (c *ClientWithResponses) RequestRescheduleTrainingWithBodyWithResponse(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*RequestRescheduleTrainingResponse, error) { + rsp, err := c.RequestRescheduleTrainingWithBody(ctx, trainingUUID, contentType, body) + if err != nil { + return nil, err + } + return ParseRequestRescheduleTrainingResponse(rsp) +} + +func (c *ClientWithResponses) RequestRescheduleTrainingWithResponse(ctx context.Context, trainingUUID string, body RequestRescheduleTrainingJSONRequestBody) (*RequestRescheduleTrainingResponse, error) { + rsp, err := c.RequestRescheduleTraining(ctx, trainingUUID, body) + if err != nil { + return nil, err + } + return ParseRequestRescheduleTrainingResponse(rsp) +} + +// RescheduleTrainingWithBodyWithResponse request with arbitrary body returning *RescheduleTrainingResponse +func (c *ClientWithResponses) RescheduleTrainingWithBodyWithResponse(ctx context.Context, trainingUUID string, contentType string, body io.Reader) (*RescheduleTrainingResponse, error) { + rsp, err := c.RescheduleTrainingWithBody(ctx, trainingUUID, contentType, body) + if err != nil { + return nil, err + } + return ParseRescheduleTrainingResponse(rsp) +} + +func (c *ClientWithResponses) RescheduleTrainingWithResponse(ctx context.Context, trainingUUID string, body RescheduleTrainingJSONRequestBody) (*RescheduleTrainingResponse, error) { + rsp, err := c.RescheduleTraining(ctx, trainingUUID, body) + if err != nil { + return nil, err + } + return ParseRescheduleTrainingResponse(rsp) +} + +// ParseGetTrainingsResponse parses an HTTP response from a GetTrainingsWithResponse call +func ParseGetTrainingsResponse(rsp *http.Response) (*GetTrainingsResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &GetTrainingsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Trainings + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseCreateTrainingResponse parses an HTTP response from a CreateTrainingWithResponse call +func ParseCreateTrainingResponse(rsp *http.Response) (*CreateTrainingResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &CreateTrainingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseCancelTrainingResponse parses an HTTP response from a CancelTrainingWithResponse call +func ParseCancelTrainingResponse(rsp *http.Response) (*CancelTrainingResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &CancelTrainingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseApproveRescheduleTrainingResponse parses an HTTP response from a ApproveRescheduleTrainingWithResponse call +func ParseApproveRescheduleTrainingResponse(rsp *http.Response) (*ApproveRescheduleTrainingResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &ApproveRescheduleTrainingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseRejectRescheduleTrainingResponse parses an HTTP response from a RejectRescheduleTrainingWithResponse call +func ParseRejectRescheduleTrainingResponse(rsp *http.Response) (*RejectRescheduleTrainingResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &RejectRescheduleTrainingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseRequestRescheduleTrainingResponse parses an HTTP response from a RequestRescheduleTrainingWithResponse call +func ParseRequestRescheduleTrainingResponse(rsp *http.Response) (*RequestRescheduleTrainingResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &RequestRescheduleTrainingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseRescheduleTrainingResponse parses an HTTP response from a RescheduleTrainingWithResponse call +func ParseRescheduleTrainingResponse(rsp *http.Response) (*RescheduleTrainingResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &RescheduleTrainingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json"): + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} diff --git a/internal/common/client/trainings/openapi_types.gen.go b/internal/common/client/trainings/openapi_types.gen.go new file mode 100644 index 0000000..10cb09b --- /dev/null +++ b/internal/common/client/trainings/openapi_types.gen.go @@ -0,0 +1,56 @@ +// Package trainings provides primitives to interact the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. +package trainings + +import ( + "time" +) + +// Error defines model for Error. +type Error struct { + Message string `json:"message"` + Slug string `json:"slug"` +} + +// PostTraining defines model for PostTraining. +type PostTraining struct { + Notes string `json:"notes"` + Time time.Time `json:"time"` +} + +// Training defines model for Training. +type Training struct { + CanBeCancelled bool `json:"canBeCancelled"` + MoveProposedBy *string `json:"moveProposedBy,omitempty"` + MoveRequiresAccept bool `json:"moveRequiresAccept"` + Notes string `json:"notes"` + ProposedTime *time.Time `json:"proposedTime,omitempty"` + Time time.Time `json:"time"` + User string `json:"user"` + UserUuid string `json:"userUuid"` + Uuid string `json:"uuid"` +} + +// Trainings defines model for Trainings. +type Trainings struct { + Trainings []Training `json:"trainings"` +} + +// CreateTrainingJSONBody defines parameters for CreateTraining. +type CreateTrainingJSONBody PostTraining + +// RequestRescheduleTrainingJSONBody defines parameters for RequestRescheduleTraining. +type RequestRescheduleTrainingJSONBody PostTraining + +// RescheduleTrainingJSONBody defines parameters for RescheduleTraining. +type RescheduleTrainingJSONBody PostTraining + +// CreateTrainingRequestBody defines body for CreateTraining for application/json ContentType. +type CreateTrainingJSONRequestBody CreateTrainingJSONBody + +// RequestRescheduleTrainingRequestBody defines body for RequestRescheduleTraining for application/json ContentType. +type RequestRescheduleTrainingJSONRequestBody RequestRescheduleTrainingJSONBody + +// RescheduleTrainingRequestBody defines body for RescheduleTraining for application/json ContentType. +type RescheduleTrainingJSONRequestBody RescheduleTrainingJSONBody diff --git a/internal/common/client/users/openapi_client_gen.go b/internal/common/client/users/openapi_client_gen.go new file mode 100644 index 0000000..01100f4 --- /dev/null +++ b/internal/common/client/users/openapi_client_gen.go @@ -0,0 +1,221 @@ +// Package users provides primitives to interact the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. +package users + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A callback for modifying requests which are generated before sending over + // the network. + RequestEditor RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = http.DefaultClient + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditor = fn + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetCurrentUser request + GetCurrentUser(ctx context.Context) (*http.Response, error) +} + +func (c *Client) GetCurrentUser(ctx context.Context) (*http.Response, error) { + req, err := NewGetCurrentUserRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if c.RequestEditor != nil { + err = c.RequestEditor(ctx, req) + if err != nil { + return nil, err + } + } + return c.Client.Do(req) +} + +// NewGetCurrentUserRequest generates requests for GetCurrentUser +func NewGetCurrentUserRequest(server string) (*http.Request, error) { + var err error + + queryUrl, err := url.Parse(server) + if err != nil { + return nil, err + } + + basePath := fmt.Sprintf("/users/current") + if basePath[0] == '/' { + basePath = basePath[1:] + } + + queryUrl, err = queryUrl.Parse(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryUrl.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetCurrentUser request + GetCurrentUserWithResponse(ctx context.Context) (*GetCurrentUserResponse, error) +} + +type GetCurrentUserResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *User +} + +// Status returns HTTPResponse.Status +func (r GetCurrentUserResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetCurrentUserResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GetCurrentUserWithResponse request returning *GetCurrentUserResponse +func (c *ClientWithResponses) GetCurrentUserWithResponse(ctx context.Context) (*GetCurrentUserResponse, error) { + rsp, err := c.GetCurrentUser(ctx) + if err != nil { + return nil, err + } + return ParseGetCurrentUserResponse(rsp) +} + +// ParseGetCurrentUserResponse parses an HTTP response from a GetCurrentUserWithResponse call +func ParseGetCurrentUserResponse(rsp *http.Response) (*GetCurrentUserResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer rsp.Body.Close() + if err != nil { + return nil, err + } + + response := &GetCurrentUserResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest User + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} diff --git a/internal/common/client/users/openapi_types.gen.go b/internal/common/client/users/openapi_types.gen.go new file mode 100644 index 0000000..7f2cbe8 --- /dev/null +++ b/internal/common/client/users/openapi_types.gen.go @@ -0,0 +1,11 @@ +// Package users provides primitives to interact the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. +package users + +// User defines model for User. +type User struct { + Balance int `json:"balance"` + DisplayName string `json:"displayName"` + Role string `json:"role"` +} diff --git a/internal/common/go.mod b/internal/common/go.mod index 7addfa1..59570cf 100644 --- a/internal/common/go.mod +++ b/internal/common/go.mod @@ -5,11 +5,13 @@ go 1.14 require ( cloud.google.com/go v0.38.0 firebase.google.com/go v3.12.0+incompatible + github.com/deepmap/oapi-codegen v1.4.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-chi/chi v4.1.0+incompatible github.com/go-chi/cors v1.0.1 github.com/go-chi/render v1.0.1 github.com/golang/protobuf v1.3.3 + github.com/google/uuid v1.1.2 github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 github.com/mattn/go-colorable v0.1.6 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect @@ -17,6 +19,7 @@ require ( github.com/onsi/gomega v1.9.0 // indirect github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.5.0 + github.com/stretchr/testify v1.5.1 github.com/x-cray/logrus-prefixed-formatter v0.5.2 go.opencensus.io v0.22.3 // indirect google.golang.org/api v0.21.0 diff --git a/internal/common/go.sum b/internal/common/go.sum index 21d4484..caf4a46 100644 --- a/internal/common/go.sum +++ b/internal/common/go.sum @@ -9,9 +9,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen v1.4.1 h1:GAsp9N5SLYhQTKo+G4iZAJYzHBySpMh3ZP7Lml9aXj0= +github.com/deepmap/oapi-codegen v1.4.1/go.mod h1:1jY0YDxfBF3tXk1u3sARJMSUJa9wV0UrVT6o+2mr/zQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -20,6 +23,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/getkin/kin-openapi v0.26.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/cors v1.0.1 h1:56TT/uWGoLWZpnMI/AwAmCneikXr5eLsiIq27wrKecw= @@ -41,6 +47,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -49,6 +56,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -64,8 +73,21 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= @@ -91,6 +113,11 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -101,6 +128,9 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4= +golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -114,9 +144,12 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI= +golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -131,10 +164,15 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -153,6 +191,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -178,6 +217,8 @@ google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -186,6 +227,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/common/server/grpc.go b/internal/common/server/grpc.go index ffc8b07..626a3ae 100644 --- a/internal/common/server/grpc.go +++ b/internal/common/server/grpc.go @@ -17,8 +17,11 @@ func RunGRPCServer(registerServer func(server *grpc.Server)) { if port == "" { port = "8080" } - grpcEndpoint := fmt.Sprintf(":%s", port) + addr := fmt.Sprintf(":%s", port) + RunGRPCServerOnAddr(addr, registerServer) +} +func RunGRPCServerOnAddr(addr string, registerServer func(server *grpc.Server)) { logrusEntry := logrus.NewEntry(logrus.StandardLogger()) grpc_logrus.ReplaceGrpcLogger(logrusEntry) @@ -34,10 +37,10 @@ func RunGRPCServer(registerServer func(server *grpc.Server)) { ) registerServer(grpcServer) - listen, err := net.Listen("tcp", grpcEndpoint) + listen, err := net.Listen("tcp", addr) if err != nil { logrus.Fatal(err) } - logrus.WithField("grpcEndpoint", grpcEndpoint).Info("Starting: gRPC Listener") + logrus.WithField("grpcEndpoint", addr).Info("Starting: gRPC Listener") logrus.Fatal(grpcServer.Serve(listen)) } diff --git a/internal/common/server/http.go b/internal/common/server/http.go index 4726f09..5cdd635 100644 --- a/internal/common/server/http.go +++ b/internal/common/server/http.go @@ -18,6 +18,10 @@ import ( ) func RunHTTPServer(createHandler func(router chi.Router) http.Handler) { + RunHTTPServerOnAddr(":"+os.Getenv("PORT"), createHandler) +} + +func RunHTTPServerOnAddr(addr string, createHandler func(router chi.Router) http.Handler) { apiRouter := chi.NewRouter() setMiddlewares(apiRouter) @@ -27,7 +31,7 @@ func RunHTTPServer(createHandler func(router chi.Router) http.Handler) { logrus.Info("Starting HTTP server") - http.ListenAndServe(":"+os.Getenv("PORT"), rootRouter) + http.ListenAndServe(addr, rootRouter) } func setMiddlewares(router *chi.Mux) { diff --git a/internal/common/tests/clients.go b/internal/common/tests/clients.go new file mode 100644 index 0000000..6ad303f --- /dev/null +++ b/internal/common/tests/clients.go @@ -0,0 +1,153 @@ +package tests + +import ( + "context" + "fmt" + "net/http" + "os" + "strings" + "testing" + "time" + + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client/trainer" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client/trainings" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client/users" + "github.com/stretchr/testify/require" +) + +func authorizationBearer(token string) func(context.Context, *http.Request) error { + return func(ctx context.Context, req *http.Request) error { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) + return nil + } +} + +type TrainerHTTPClient struct { + client *trainer.ClientWithResponses +} + +func NewTrainerHTTPClient(t *testing.T, token string) TrainerHTTPClient { + url := fmt.Sprintf("http://%v/api", os.Getenv("TRAINER_HTTP_ADDR")) + + client, err := trainer.NewClientWithResponses( + url, + trainer.WithRequestEditorFn(authorizationBearer(token)), + ) + require.NoError(t, err) + + return TrainerHTTPClient{ + client: client, + } +} + +func (c TrainerHTTPClient) MakeHourAvailable(t *testing.T, hour time.Time) int { + response, err := c.client.MakeHourAvailable(context.Background(), trainer.MakeHourAvailableJSONRequestBody{ + Hours: []time.Time{hour}, + }) + require.NoError(t, err) + return response.StatusCode +} + +func (c TrainerHTTPClient) MakeHourUnavailable(t *testing.T, hour time.Time) { + response, err := c.client.MakeHourUnavailable(context.Background(), trainer.MakeHourUnavailableJSONRequestBody{ + Hours: []time.Time{hour}, + }) + require.NoError(t, err) + require.Equal(t, http.StatusNoContent, response.StatusCode) +} + +func (c TrainerHTTPClient) GetTrainerAvailableHours(t *testing.T, from time.Time, to time.Time) []trainer.Date { + response, err := c.client.GetTrainerAvailableHoursWithResponse(context.Background(), &trainer.GetTrainerAvailableHoursParams{ + DateFrom: from, + DateTo: to, + }) + require.NoError(t, err) + require.Equal(t, http.StatusOK, response.StatusCode()) + + return *response.JSON200 +} + +type TrainingsHTTPClient struct { + client *trainings.ClientWithResponses +} + +func NewTrainingsHTTPClient(t *testing.T, token string) TrainingsHTTPClient { + url := fmt.Sprintf("http://%v/api", os.Getenv("TRAININGS_HTTP_ADDR")) + + client, err := trainings.NewClientWithResponses( + url, + trainings.WithRequestEditorFn(authorizationBearer(token)), + ) + require.NoError(t, err) + + return TrainingsHTTPClient{ + client: client, + } +} + +func (c TrainingsHTTPClient) CreateTraining(t *testing.T, note string, hour time.Time) string { + response, err := c.client.CreateTrainingWithResponse(context.Background(), trainings.CreateTrainingJSONRequestBody{ + Notes: note, + Time: hour, + }) + require.NoError(t, err) + require.Equal(t, http.StatusNoContent, response.StatusCode()) + + contentLocation := response.HTTPResponse.Header.Get("content-location") + + return lastPathElement(contentLocation) +} + +func (c TrainingsHTTPClient) CreateTrainingShouldFail(t *testing.T, note string, hour time.Time) { + response, err := c.client.CreateTraining(context.Background(), trainings.CreateTrainingJSONRequestBody{ + Notes: note, + Time: hour, + }) + require.NoError(t, err) + require.Equal(t, http.StatusInternalServerError, response.StatusCode) +} + +func (c TrainingsHTTPClient) GetTrainings(t *testing.T) trainings.Trainings { + response, err := c.client.GetTrainingsWithResponse(context.Background()) + require.NoError(t, err) + require.Equal(t, http.StatusOK, response.StatusCode()) + + return *response.JSON200 +} + +func (c TrainingsHTTPClient) CancelTraining(t *testing.T, trainingUUID string, expectedStatusCode int) { + response, err := c.client.CancelTraining(context.Background(), trainingUUID) + require.NoError(t, err) + require.Equal(t, expectedStatusCode, response.StatusCode) +} + +type UsersHTTPClient struct { + client *users.ClientWithResponses +} + +func NewUsersHTTPClient(t *testing.T, token string) UsersHTTPClient { + url := fmt.Sprintf("http://%v/api", os.Getenv("USERS_HTTP_ADDR")) + + client, err := users.NewClientWithResponses( + url, + users.WithRequestEditorFn(authorizationBearer(token)), + ) + require.NoError(t, err) + + return UsersHTTPClient{ + client: client, + } +} + +func (c UsersHTTPClient) GetCurrentUser(t *testing.T) users.User { + response, err := c.client.GetCurrentUserWithResponse(context.Background()) + require.NoError(t, err) + require.Equal(t, http.StatusOK, response.StatusCode()) + + return *response.JSON200 +} + +func lastPathElement(path string) string { + parts := strings.Split(path, "/") + return parts[len(parts)-1] +} diff --git a/internal/common/tests/e2e_test.go b/internal/common/tests/e2e_test.go new file mode 100644 index 0000000..0b7b35b --- /dev/null +++ b/internal/common/tests/e2e_test.go @@ -0,0 +1,69 @@ +package tests + +import ( + "context" + "testing" + + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/genproto/users" + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +func TestCreateTraining(t *testing.T) { + t.Parallel() + + hour := RelativeDate(12, 12) + + userID := "TestCreateTraining-user" + trainerJWT := FakeTrainerJWT(t, uuid.New().String()) + attendeeJWT := FakeAttendeeJWT(t, userID) + trainerHTTPClient := NewTrainerHTTPClient(t, trainerJWT) + trainingsHTTPClient := NewTrainingsHTTPClient(t, attendeeJWT) + usersHTTPClient := NewUsersHTTPClient(t, attendeeJWT) + + usersGrpcClient, _, err := client.NewUsersClient() + require.NoError(t, err) + + // Cancel the training if exists and make the hour available + trainings := trainingsHTTPClient.GetTrainings(t) + for _, training := range trainings.Trainings { + if training.Time.Equal(hour) { + trainingsTrainerHTTPClient := NewTrainingsHTTPClient(t, trainerJWT) + trainingsTrainerHTTPClient.CancelTraining(t, training.Uuid, 200) + break + } + } + hours := trainerHTTPClient.GetTrainerAvailableHours(t, hour, hour) + if len(hours) > 0 { + for _, h := range hours[0].Hours { + if h.Hour.Equal(hour) { + trainerHTTPClient.MakeHourUnavailable(t, hour) + break + } + } + } + + trainerHTTPClient.MakeHourAvailable(t, hour) + + user := usersHTTPClient.GetCurrentUser(t) + originalBalance := user.Balance + + _, err = usersGrpcClient.UpdateTrainingBalance(context.Background(), &users.UpdateTrainingBalanceRequest{ + UserId: userID, + AmountChange: 1, + }) + require.NoError(t, err) + + user = usersHTTPClient.GetCurrentUser(t) + require.Equal(t, originalBalance+1, user.Balance, "Attendee's balance should be updated") + + trainingUUID := trainingsHTTPClient.CreateTraining(t, "some note", hour) + + trainingsResponse := trainingsHTTPClient.GetTrainings(t) + require.Len(t, trainingsResponse.Trainings, 1) + require.Equal(t, trainingUUID, trainingsResponse.Trainings[0].Uuid, "Attendee should see the training") + + user = usersHTTPClient.GetCurrentUser(t) + require.Equal(t, originalBalance, user.Balance, "Attendee's balance should be updated after a training is scheduled") +} diff --git a/internal/common/tests/hours.go b/internal/common/tests/hours.go new file mode 100644 index 0000000..6c5671d --- /dev/null +++ b/internal/common/tests/hours.go @@ -0,0 +1,15 @@ +package tests + +import "time" + +// RelativeDate returns a date in the future in specified days and hours. +// This allows running the tests in parallel, since each tests uses different date and hour. +// +// The downside of this approach is that you need to be aware of used dates when adding a new test. +// In our case this is not an issue, as it's trivial to see all usages and there's just a few of them. +// +// Another, more complex approach, would be to use random dates and retry in case of an error. +func RelativeDate(days int, hour int) time.Time { + now := time.Now().UTC().AddDate(0, 0, days) + return time.Date(now.Year(), now.Month(), now.Day(), hour, 0, 0, 0, time.UTC) +} diff --git a/internal/common/tests/jwt.go b/internal/common/tests/jwt.go new file mode 100644 index 0000000..417e98f --- /dev/null +++ b/internal/common/tests/jwt.go @@ -0,0 +1,35 @@ +package tests + +import ( + "testing" + + "github.com/dgrijalva/jwt-go" + "github.com/stretchr/testify/require" +) + +func FakeAttendeeJWT(t *testing.T, userID string) string { + return fakeJWT(t, jwt.MapClaims{ + "user_uuid": userID, + "email": "attendee@threedots.tech", + "role": "attendee", + "name": "Attendee", + }) +} + +func FakeTrainerJWT(t *testing.T, userID string) string { + return fakeJWT(t, jwt.MapClaims{ + "user_uuid": userID, + "email": "trainer@threedots.tech", + "role": "trainer", + "name": "Trainer", + }) +} + +func fakeJWT(t *testing.T, claims jwt.MapClaims) string { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + tokenString, err := token.SignedString([]byte("mock_secret")) + require.NoError(t, err) + + return tokenString +} diff --git a/internal/common/tests/wait.go b/internal/common/tests/wait.go new file mode 100644 index 0000000..d1f510b --- /dev/null +++ b/internal/common/tests/wait.go @@ -0,0 +1,33 @@ +package tests + +import ( + "net" + "time" +) + +func WaitForPort(address string) bool { + waitChan := make(chan struct{}) + + go func() { + for { + conn, err := net.DialTimeout("tcp", address, time.Second) + if err != nil { + time.Sleep(time.Second) + continue + } + + if conn != nil { + waitChan <- struct{}{} + return + } + } + }() + + timeout := time.After(5 * time.Second) + select { + case <-waitChan: + return true + case <-timeout: + return false + } +} diff --git a/internal/trainer/adapters/hour_mysql_repository.go b/internal/trainer/adapters/hour_mysql_repository.go index d10533c..3857675 100644 --- a/internal/trainer/adapters/hour_mysql_repository.go +++ b/internal/trainer/adapters/hour_mysql_repository.go @@ -89,7 +89,7 @@ func (m MySQLHourRepository) UpdateHour( for { err := m.updateHour(ctx, hourTime, updateFn) - if val, ok := err.(*mysql.MySQLError); ok && val.Number == mySQLDeadlockErrorCode { + if val, ok := errors.Cause(err).(*mysql.MySQLError); ok && val.Number == mySQLDeadlockErrorCode { continue } diff --git a/internal/trainer/go.mod b/internal/trainer/go.mod index df8ef8c..878bd2f 100644 --- a/internal/trainer/go.mod +++ b/internal/trainer/go.mod @@ -5,15 +5,16 @@ go 1.14 require ( cloud.google.com/go/firestore v1.2.0 github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common v0.0.0-00010101000000-000000000000 - github.com/deepmap/oapi-codegen v1.3.6 + github.com/deepmap/oapi-codegen v1.4.1 github.com/go-chi/chi v4.1.0+incompatible github.com/go-chi/render v1.0.1 github.com/go-sql-driver/mysql v1.4.0 github.com/golang/protobuf v1.3.5 + github.com/google/uuid v1.1.2 github.com/jmoiron/sqlx v1.2.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.5.0 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 go.uber.org/multierr v1.1.0 golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect google.golang.org/api v0.21.0 diff --git a/internal/trainer/go.sum b/internal/trainer/go.sum index 8bc6c3e..a226374 100644 --- a/internal/trainer/go.sum +++ b/internal/trainer/go.sum @@ -36,6 +36,7 @@ firebase.google.com/go v3.12.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ThreeDotsLabs/wild-workouts-go-ddd-example v0.0.0-20201128134131-231817a48362 h1:VBPB/xIjsOeQJsWgh5Fgz14kk5lSBSUo7EiFLdFbkPc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -46,16 +47,17 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.3.6 h1:Wj44p9A0V0PJ+AUg0BWdyGcsS1LY18U+0rCuPQgK0+o= -github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU= +github.com/deepmap/oapi-codegen v1.4.1 h1:GAsp9N5SLYhQTKo+G4iZAJYzHBySpMh3ZP7Lml9aXj0= +github.com/deepmap/oapi-codegen v1.4.1/go.mod h1:1jY0YDxfBF3tXk1u3sARJMSUJa9wV0UrVT6o+2mr/zQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk= +github.com/getkin/kin-openapi v0.26.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= @@ -72,6 +74,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -107,6 +110,8 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -114,6 +119,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= @@ -123,6 +129,7 @@ github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfE github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -134,6 +141,7 @@ github.com/labstack/echo/v4 v4.1.11 h1:z0BZoArY4FqdpUEl+wlHp4hnr/oSR6MTmQmv8OHSo github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -147,12 +155,15 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -169,9 +180,10 @@ github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= @@ -401,12 +413,14 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/trainer/main.go b/internal/trainer/main.go index 39fc9bb..4d2d085 100644 --- a/internal/trainer/main.go +++ b/internal/trainer/main.go @@ -7,16 +7,11 @@ import ( "os" "strings" - "cloud.google.com/go/firestore" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/genproto/trainer" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/logs" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/server" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/adapters" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/app" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/app/command" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/app/query" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/domain/hour" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/ports" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/service" "github.com/go-chi/chi" "google.golang.org/grpc" ) @@ -25,7 +20,8 @@ func main() { logs.Init() ctx := context.Background() - application := newApplication(ctx) + + application := service.NewApplication(ctx) serverType := strings.ToLower(os.Getenv("SERVER_TO_RUN")) switch serverType { @@ -47,38 +43,3 @@ func main() { panic(fmt.Sprintf("server type '%s' is not supported", serverType)) } } - -func newApplication(ctx context.Context) app.Application { - firestoreClient, err := firestore.NewClient(ctx, os.Getenv("GCP_PROJECT")) - if err != nil { - panic(err) - } - - factoryConfig := hour.FactoryConfig{ - MaxWeeksInTheFutureToSet: 6, - MinUtcHour: 12, - MaxUtcHour: 20, - } - - datesRepository := adapters.NewDatesFirestoreRepository(firestoreClient, factoryConfig) - - hourFactory, err := hour.NewFactory(factoryConfig) - if err != nil { - panic(err) - } - - hourRepository := adapters.NewFirestoreHourRepository(firestoreClient, hourFactory) - - return app.Application{ - Commands: app.Commands{ - CancelTraining: command.NewCancelTrainingHandler(hourRepository), - ScheduleTraining: command.NewScheduleTrainingHandler(hourRepository), - MakeHoursAvailable: command.NewMakeHoursAvailableHandler(hourRepository), - MakeHoursUnavailable: command.NewMakeHoursUnavailableHandler(hourRepository), - }, - Queries: app.Queries{ - HourAvailability: query.NewHourAvailabilityHandler(hourRepository), - TrainerAvailableHours: query.NewAvailableHoursHandler(datesRepository), - }, - } -} diff --git a/internal/trainer/service/application.go b/internal/trainer/service/application.go new file mode 100644 index 0000000..7bec252 --- /dev/null +++ b/internal/trainer/service/application.go @@ -0,0 +1,48 @@ +package service + +import ( + "context" + "os" + + "cloud.google.com/go/firestore" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/adapters" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/app" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/app/command" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/app/query" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/domain/hour" +) + +func NewApplication(ctx context.Context) app.Application { + firestoreClient, err := firestore.NewClient(ctx, os.Getenv("GCP_PROJECT")) + if err != nil { + panic(err) + } + + factoryConfig := hour.FactoryConfig{ + MaxWeeksInTheFutureToSet: 6, + MinUtcHour: 12, + MaxUtcHour: 20, + } + + datesRepository := adapters.NewDatesFirestoreRepository(firestoreClient, factoryConfig) + + hourFactory, err := hour.NewFactory(factoryConfig) + if err != nil { + panic(err) + } + + hourRepository := adapters.NewFirestoreHourRepository(firestoreClient, hourFactory) + + return app.Application{ + Commands: app.Commands{ + CancelTraining: command.NewCancelTrainingHandler(hourRepository), + ScheduleTraining: command.NewScheduleTrainingHandler(hourRepository), + MakeHoursAvailable: command.NewMakeHoursAvailableHandler(hourRepository), + MakeHoursUnavailable: command.NewMakeHoursUnavailableHandler(hourRepository), + }, + Queries: app.Queries{ + HourAvailability: query.NewHourAvailabilityHandler(hourRepository), + TrainerAvailableHours: query.NewAvailableHoursHandler(datesRepository), + }, + } +} diff --git a/internal/trainer/service/component_test.go b/internal/trainer/service/component_test.go new file mode 100644 index 0000000..e77fd00 --- /dev/null +++ b/internal/trainer/service/component_test.go @@ -0,0 +1,108 @@ +package service + +import ( + "context" + "log" + "net/http" + "os" + "testing" + "time" + + trainerHTTP "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client/trainer" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/genproto/trainer" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/server" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/tests" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer/ports" + "github.com/go-chi/chi" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +func TestHoursAvailability(t *testing.T) { + t.Parallel() + + token := tests.FakeTrainerJWT(t, uuid.New().String()) + client := tests.NewTrainerHTTPClient(t, token) + + hour := tests.RelativeDate(11, 12) + expectedHour := trainerHTTP.Hour{ + Available: true, + HasTrainingScheduled: false, + Hour: hour, + } + + date := hour.Truncate(24 * time.Hour) + from := date.AddDate(0, 0, -1) + to := date.AddDate(0, 0, 1) + + getHours := func() []trainerHTTP.Hour { + dates := client.GetTrainerAvailableHours(t, from, to) + for _, d := range dates { + if d.Date.Equal(date) { + return d.Hours + } + } + t.Fatalf("Date not found in dates: %+v", dates) + return nil + } + + client.MakeHourUnavailable(t, hour) + require.NotContains(t, getHours(), expectedHour) + + code := client.MakeHourAvailable(t, hour) + require.Equal(t, http.StatusNoContent, code) + require.Contains(t, getHours(), expectedHour) + + client.MakeHourUnavailable(t, hour) + require.NotContains(t, getHours(), expectedHour) +} + +func TestUnauthorizedForAttendee(t *testing.T) { + t.Parallel() + + token := tests.FakeAttendeeJWT(t, uuid.New().String()) + client := tests.NewTrainerHTTPClient(t, token) + + hour := tests.RelativeDate(11, 13) + + code := client.MakeHourAvailable(t, hour) + require.Equal(t, http.StatusUnauthorized, code) +} + +func startService() bool { + app := NewApplication(context.Background()) + + trainerHTTPAddr := os.Getenv("TRAINER_HTTP_ADDR") + go server.RunHTTPServerOnAddr(trainerHTTPAddr, func(router chi.Router) http.Handler { + return ports.HandlerFromMux(ports.NewHttpServer(app), router) + }) + + trainerGrpcAddr := os.Getenv("TRAINER_GRPC_ADDR") + go server.RunGRPCServerOnAddr(trainerGrpcAddr, func(server *grpc.Server) { + svc := ports.NewGrpcServer(app) + trainer.RegisterTrainerServiceServer(server, svc) + }) + + ok := tests.WaitForPort(trainerHTTPAddr) + if !ok { + log.Println("Timed out waiting for trainer HTTP to come up") + return false + } + + ok = tests.WaitForPort(trainerGrpcAddr) + if !ok { + log.Println("Timed out waiting for trainer gRPC to come up") + } + + return ok +} + +func TestMain(m *testing.M) { + if !startService() { + log.Println("Timed out waiting for trainings HTTP to come up") + os.Exit(1) + } + + os.Exit(m.Run()) +} diff --git a/internal/trainings/adapters/trainings_firestore_repository_test.go b/internal/trainings/adapters/trainings_firestore_repository_test.go index 6dfe443..4e5db5e 100644 --- a/internal/trainings/adapters/trainings_firestore_repository_test.go +++ b/internal/trainings/adapters/trainings_firestore_repository_test.go @@ -213,7 +213,16 @@ func TestTrainingsFirestoreRepository_AllTrainings(t *testing.T) { }, } - assertQueryTrainingsEquals(t, expectedTrainings, trainings) + var filteredTrainings []query.Training + for _, tr := range trainings { + for _, ex := range expectedTrainings { + if tr.UUID == ex.UUID { + filteredTrainings = append(filteredTrainings, tr) + } + } + } + + assertQueryTrainingsEquals(t, expectedTrainings, filteredTrainings) } func TestTrainingsFirestoreRepository_FindTrainingsForUser(t *testing.T) { @@ -282,6 +291,7 @@ func newRandomTrainingTime() time.Time { } func newExampleTraining(t *testing.T) *training.Training { + t.Helper() tr, err := training.NewTraining( uuid.New().String(), uuid.New().String(), @@ -294,6 +304,7 @@ func newExampleTraining(t *testing.T) *training.Training { } func newCanceledTraining(t *testing.T) *training.Training { + t.Helper() tr, err := training.NewTraining( uuid.New().String(), uuid.New().String(), @@ -309,6 +320,7 @@ func newCanceledTraining(t *testing.T) *training.Training { } func newTrainingWithNote(t *testing.T) *training.Training { + t.Helper() tr := newExampleTraining(t) err := tr.UpdateNotes("foo") require.NoError(t, err) @@ -317,6 +329,7 @@ func newTrainingWithNote(t *testing.T) *training.Training { } func newTrainingWithProposedReschedule(t *testing.T) *training.Training { + t.Helper() tr := newExampleTraining(t) tr.ProposeReschedule(time.Now().AddDate(0, 0, 14), training.Trainer) @@ -324,6 +337,7 @@ func newTrainingWithProposedReschedule(t *testing.T) *training.Training { } func assertPersistedTrainingEquals(t *testing.T, repo adapters.TrainingsFirestoreRepository, tr *training.Training) { + t.Helper() persistedTraining, err := repo.GetTraining( context.Background(), tr.UUID(), @@ -340,6 +354,7 @@ var cmpRoundTimeOpt = cmp.Comparer(func(x, y time.Time) bool { }) func assertTrainingsEquals(t *testing.T, tr1, tr2 *training.Training) { + t.Helper() cmpOpts := []cmp.Option{ cmpRoundTimeOpt, cmp.AllowUnexported( @@ -357,6 +372,7 @@ func assertTrainingsEquals(t *testing.T, tr1, tr2 *training.Training) { } func assertQueryTrainingsEquals(t *testing.T, expectedTrainings, trainings []query.Training) bool { + t.Helper() cmpOpts := []cmp.Option{ cmpRoundTimeOpt, cmpopts.SortSlices(func(x, y query.Training) bool { @@ -370,6 +386,7 @@ func assertQueryTrainingsEquals(t *testing.T, expectedTrainings, trainings []que } func newFirebaseRepository(t *testing.T) adapters.TrainingsFirestoreRepository { + t.Helper() firestoreClient, err := firestore.NewClient(context.Background(), os.Getenv("GCP_PROJECT")) require.NoError(t, err) diff --git a/internal/trainings/go.mod b/internal/trainings/go.mod index bd2b2a1..abb49bd 100644 --- a/internal/trainings/go.mod +++ b/internal/trainings/go.mod @@ -4,17 +4,15 @@ go 1.14 require ( cloud.google.com/go v0.38.0 - firebase.google.com/go v3.12.0+incompatible // indirect github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common v0.0.0-00010101000000-000000000000 - github.com/deepmap/oapi-codegen v1.3.6 + github.com/deepmap/oapi-codegen v1.4.1 github.com/go-chi/chi v4.1.0+incompatible github.com/go-chi/render v1.0.1 github.com/golang/protobuf v1.3.3 github.com/google/go-cmp v0.5.2 - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.1.2 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.5.0 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 google.golang.org/api v0.21.0 google.golang.org/grpc v1.28.0 ) diff --git a/internal/trainings/go.sum b/internal/trainings/go.sum index 38b97af..1946acb 100644 --- a/internal/trainings/go.sum +++ b/internal/trainings/go.sum @@ -3,12 +3,9 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -firebase.google.com/go v1.0.2 h1:MCEmjmlwZiQ0s+z7EDVX6e3KHHvpGdF2pJBiQAXVXao= firebase.google.com/go v3.12.0+incompatible h1:q70KCp/J0oOL8kJ8oV2j3646kV4TB8Y5IvxXC0WT1bo= firebase.google.com/go v3.12.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/ThreeDotsLabs/wild-workouts-go-ddd-example v0.0.0-20200902144722-a638f03bd640 h1:sy3JKJwBOMpKzLcz/NZ6VEH3gVaNt7AaYJWxAgb0dfk= -github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainer v0.0.0-20200902144722-a638f03bd640 h1:Z3bPsxkWhiJQMT6KjeyTLHhXMIwpZ4WVGYCbIm2HfSY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -16,16 +13,17 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.3.6 h1:Wj44p9A0V0PJ+AUg0BWdyGcsS1LY18U+0rCuPQgK0+o= -github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU= +github.com/deepmap/oapi-codegen v1.4.1 h1:GAsp9N5SLYhQTKo+G4iZAJYzHBySpMh3ZP7Lml9aXj0= +github.com/deepmap/oapi-codegen v1.4.1/go.mod h1:1jY0YDxfBF3tXk1u3sARJMSUJa9wV0UrVT6o+2mr/zQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk= +github.com/getkin/kin-openapi v0.26.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= @@ -37,6 +35,7 @@ github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= @@ -58,8 +57,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -68,10 +67,12 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9G github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -98,8 +99,10 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -115,9 +118,10 @@ github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= @@ -192,6 +196,7 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.21.0 h1:zS+Q/CJJnVlXpXQVIz+lH0ZT2lBuT2ac7XD8Y/3w6hY= @@ -216,12 +221,14 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/trainings/main.go b/internal/trainings/main.go index 6cdbeb9..50c98ab 100644 --- a/internal/trainings/main.go +++ b/internal/trainings/main.go @@ -3,17 +3,11 @@ package main import ( "context" "net/http" - "os" - "cloud.google.com/go/firestore" - grpcClient "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/logs" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/server" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/adapters" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/app" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/app/command" - "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/app/query" "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/ports" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/service" "github.com/go-chi/chi" ) @@ -22,49 +16,10 @@ func main() { ctx := context.Background() - app, cleanup := newApplication(ctx) + app, cleanup := service.NewApplication(ctx) defer cleanup() server.RunHTTPServer(func(router chi.Router) http.Handler { return ports.HandlerFromMux(ports.NewHttpServer(app), router) }) } - -func newApplication(ctx context.Context) (app.Application, func()) { - client, err := firestore.NewClient(ctx, os.Getenv("GCP_PROJECT")) - if err != nil { - panic(err) - } - - trainerClient, closeTrainerClient, err := grpcClient.NewTrainerClient() - if err != nil { - panic(err) - } - - usersClient, closeUsersClient, err := grpcClient.NewUsersClient() - if err != nil { - panic(err) - } - - trainingsRepository := adapters.NewTrainingsFirestoreRepository(client) - trainerGrpc := adapters.NewTrainerGrpc(trainerClient) - usersGrpc := adapters.NewUsersGrpc(usersClient) - - return app.Application{ - Commands: app.Commands{ - ApproveTrainingReschedule: command.NewApproveTrainingRescheduleHandler(trainingsRepository, usersGrpc, trainerGrpc), - CancelTraining: command.NewCancelTrainingHandler(trainingsRepository, usersGrpc, trainerGrpc), - RejectTrainingReschedule: command.NewRejectTrainingRescheduleHandler(trainingsRepository), - RescheduleTraining: command.NewRescheduleTrainingHandler(trainingsRepository, usersGrpc, trainerGrpc), - RequestTrainingReschedule: command.NewRequestTrainingRescheduleHandler(trainingsRepository), - ScheduleTraining: command.NewScheduleTrainingHandler(trainingsRepository, usersGrpc, trainerGrpc), - }, - Queries: app.Queries{ - AllTrainings: query.NewAllTrainingsHandler(trainingsRepository), - TrainingsForUser: query.NewTrainingsForUserHandler(trainingsRepository), - }, - }, func() { - _ = closeTrainerClient() - _ = closeUsersClient - } -} diff --git a/internal/trainings/service/component_test.go b/internal/trainings/service/component_test.go new file mode 100644 index 0000000..29e1ec5 --- /dev/null +++ b/internal/trainings/service/component_test.go @@ -0,0 +1,80 @@ +package service + +import ( + "context" + "log" + "net/http" + "os" + "testing" + + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/server" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/tests" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/ports" + "github.com/go-chi/chi" + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +func TestCreateTraining(t *testing.T) { + t.Parallel() + + token := tests.FakeAttendeeJWT(t, uuid.New().String()) + client := tests.NewTrainingsHTTPClient(t, token) + + hour := tests.RelativeDate(10, 12) + trainingUUID := client.CreateTraining(t, "some note", hour) + + trainingsResponse := client.GetTrainings(t) + + var trainingsUUIDs []string + for _, t := range trainingsResponse.Trainings { + trainingsUUIDs = append(trainingsUUIDs, t.Uuid) + } + + require.Contains(t, trainingsUUIDs, trainingUUID) +} + +func TestCancelTraining(t *testing.T) { + t.Parallel() + + token := tests.FakeAttendeeJWT(t, uuid.New().String()) + client := tests.NewTrainingsHTTPClient(t, token) + + hour := tests.RelativeDate(10, 13) + trainingUUID := client.CreateTraining(t, "some note", hour) + + client.CancelTraining(t, trainingUUID, http.StatusOK) + + trainingsResponse := client.GetTrainings(t) + + var trainingsUUIDs []string + for _, t := range trainingsResponse.Trainings { + trainingsUUIDs = append(trainingsUUIDs, t.Uuid) + } + + require.NotContains(t, trainingsUUIDs, trainingUUID) +} + +func startService() bool { + app := NewComponentTestApplication(context.Background()) + + trainingsHTTPAddr := os.Getenv("TRAININGS_HTTP_ADDR") + go server.RunHTTPServerOnAddr(trainingsHTTPAddr, func(router chi.Router) http.Handler { + return ports.HandlerFromMux(ports.NewHttpServer(app), router) + }) + + ok := tests.WaitForPort(trainingsHTTPAddr) + if !ok { + log.Println("Timed out waiting for trainings HTTP to come up") + } + + return ok +} + +func TestMain(m *testing.M) { + if !startService() { + os.Exit(1) + } + + os.Exit(m.Run()) +} diff --git a/internal/trainings/service/mocks.go b/internal/trainings/service/mocks.go new file mode 100644 index 0000000..5b73e08 --- /dev/null +++ b/internal/trainings/service/mocks.go @@ -0,0 +1,28 @@ +package service + +import ( + "context" + "time" +) + +type TrainerServiceMock struct { +} + +func (t TrainerServiceMock) ScheduleTraining(ctx context.Context, trainingTime time.Time) error { + return nil +} + +func (t TrainerServiceMock) CancelTraining(ctx context.Context, trainingTime time.Time) error { + return nil +} + +func (t TrainerServiceMock) MoveTraining(ctx context.Context, newTime time.Time, originalTrainingTime time.Time) error { + return nil +} + +type UserServiceMock struct { +} + +func (u UserServiceMock) UpdateTrainingBalance(ctx context.Context, userID string, amountChange int) error { + return nil +} diff --git a/internal/trainings/service/service.go b/internal/trainings/service/service.go new file mode 100644 index 0000000..9a7b9a7 --- /dev/null +++ b/internal/trainings/service/service.go @@ -0,0 +1,61 @@ +package service + +import ( + "context" + "os" + + "cloud.google.com/go/firestore" + grpcClient "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common/client" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/adapters" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/app" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/app/command" + "github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/trainings/app/query" +) + +func NewApplication(ctx context.Context) (app.Application, func()) { + trainerClient, closeTrainerClient, err := grpcClient.NewTrainerClient() + if err != nil { + panic(err) + } + + usersClient, closeUsersClient, err := grpcClient.NewUsersClient() + if err != nil { + panic(err) + } + trainerGrpc := adapters.NewTrainerGrpc(trainerClient) + usersGrpc := adapters.NewUsersGrpc(usersClient) + + return newApplication(ctx, trainerGrpc, usersGrpc), + func() { + _ = closeTrainerClient() + _ = closeUsersClient() + } +} + +func NewComponentTestApplication(ctx context.Context) app.Application { + return newApplication(ctx, TrainerServiceMock{}, UserServiceMock{}) +} + +func newApplication(ctx context.Context, trainerGrpc command.TrainerService, usersGrpc command.UserService) app.Application { + client, err := firestore.NewClient(ctx, os.Getenv("GCP_PROJECT")) + if err != nil { + panic(err) + } + + trainingsRepository := adapters.NewTrainingsFirestoreRepository(client) + + return app.Application{ + Commands: app.Commands{ + ApproveTrainingReschedule: command.NewApproveTrainingRescheduleHandler(trainingsRepository, usersGrpc, trainerGrpc), + CancelTraining: command.NewCancelTrainingHandler(trainingsRepository, usersGrpc, trainerGrpc), + RejectTrainingReschedule: command.NewRejectTrainingRescheduleHandler(trainingsRepository), + RescheduleTraining: command.NewRescheduleTrainingHandler(trainingsRepository, usersGrpc, trainerGrpc), + RequestTrainingReschedule: command.NewRequestTrainingRescheduleHandler(trainingsRepository), + ScheduleTraining: command.NewScheduleTrainingHandler(trainingsRepository, usersGrpc, trainerGrpc), + }, + Queries: app.Queries{ + AllTrainings: query.NewAllTrainingsHandler(trainingsRepository), + TrainingsForUser: query.NewTrainingsForUserHandler(trainingsRepository), + }, + } +} diff --git a/internal/users/go.mod b/internal/users/go.mod index b933d8e..66941d5 100644 --- a/internal/users/go.mod +++ b/internal/users/go.mod @@ -6,7 +6,6 @@ require ( cloud.google.com/go/firestore v1.2.0 firebase.google.com/go v3.12.0+incompatible github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common v0.0.0-00010101000000-000000000000 - github.com/deepmap/oapi-codegen v1.3.6 // indirect github.com/go-chi/chi v4.1.0+incompatible github.com/go-chi/render v1.0.1 github.com/pkg/errors v0.9.1 diff --git a/internal/users/go.sum b/internal/users/go.sum index 363a60d..5795104 100644 --- a/internal/users/go.sum +++ b/internal/users/go.sum @@ -14,14 +14,17 @@ cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0 h1:K2NyuHRuv15ku6eUpe0DQk5ZykPMnSOnvuVf6IHcjaE= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.2.0 h1:zrl+2VJAYC/C6WzEPnkqZIBeHyHFs/UmtzJdXU4Bvmo= cloud.google.com/go/firestore v1.2.0/go.mod h1:iISCjWnTpnoJT1R287xRdjvQHJrxQOpeah4phb5D3h0= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= @@ -43,16 +46,16 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.3.6 h1:Wj44p9A0V0PJ+AUg0BWdyGcsS1LY18U+0rCuPQgK0+o= -github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU= +github.com/deepmap/oapi-codegen v1.4.1/go.mod h1:1jY0YDxfBF3tXk1u3sARJMSUJa9wV0UrVT6o+2mr/zQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk= +github.com/getkin/kin-openapi v0.26.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= @@ -67,6 +70,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -95,6 +99,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -102,6 +107,7 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -109,6 +115,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -116,10 +123,13 @@ github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfE github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labstack/echo/v4 v4.1.11 h1:z0BZoArY4FqdpUEl+wlHp4hnr/oSR6MTmQmv8OHSoww= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= @@ -140,14 +150,17 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -157,8 +170,9 @@ github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= @@ -247,6 +261,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -382,13 +397,17 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/users/openapi_types.gen.go b/internal/users/openapi_types.gen.go index d3b6573..5e4382c 100644 --- a/internal/users/openapi_types.gen.go +++ b/internal/users/openapi_types.gen.go @@ -3,12 +3,6 @@ // Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. package main -// Error defines model for Error. -type Error struct { - Message string `json:"message"` - Slug string `json:"slug"` -} - // User defines model for User. type User struct { Balance int `json:"balance"` diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..dee7fb3 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +readonly service="$1" +readonly env_file="$2" + +cd "./internal/$service" +env $(cat "../../.env" "../../$env_file" | grep -Ev '^#' | xargs) go test -count=1 ./...