You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-17 00:17:59 +02:00
authenticator storage engines
Created a storage interface used by authenticator to support multiple types of storage types for private keys. Added a new file storage engine which is now the default for web-api. Migrated aws secrets manager to be optional.
This commit is contained in:
@ -5,6 +5,8 @@ LABEL maintainer="lee@geeksinthewoods.com"
|
||||
RUN apk --update --no-cache add \
|
||||
git
|
||||
|
||||
RUN go get -u github.com/swaggo/swag/cmd/swag
|
||||
|
||||
# go to base project
|
||||
WORKDIR $GOPATH/src/gitlab.com/geeks-accelerator/oss/saas-starter-kit/example-project
|
||||
|
||||
@ -26,6 +28,9 @@ COPY cmd/web-api/templates /templates
|
||||
|
||||
WORKDIR ./cmd/web-api
|
||||
|
||||
# Update the API documentation.
|
||||
RUN swag init
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix nocgo -o /gosrv .
|
||||
|
||||
FROM alpine:3.9
|
||||
|
26
example-project/cmd/web-api/README.md
Normal file
26
example-project/cmd/web-api/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# SaaS Web API
|
||||
|
||||
Copyright 2019, Geeks Accelerator
|
||||
accelerator@geeksinthewoods.com.com
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Service exposes a JSON api.
|
||||
|
||||
|
||||
## Local Installation
|
||||
|
||||
### Build
|
||||
```bash
|
||||
go build .
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
To build using the docker file, need to be in the project root directory. `Dockerfile` references go.mod in root directory.
|
||||
|
||||
|
||||
```bash
|
||||
docker build -f cmd/web-api/Dockerfile -t saas-web-api .
|
||||
```
|
80
example-project/cmd/web-api/docs/docs.go
Normal file
80
example-project/cmd/web-api/docs/docs.go
Normal file
@ -0,0 +1,80 @@
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag at
|
||||
// 2019-06-24 15:42:25.999684 -0800 AKDT m=+0.030714022
|
||||
|
||||
package docs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/alecthomas/template"
|
||||
"github.com/swaggo/swag"
|
||||
)
|
||||
|
||||
var doc = `{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server celler server.",
|
||||
"title": "SaaS Example API",
|
||||
"termsOfService": "http://geeksinthewoods.com/terms",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "https://gitlab.com/geeks-accelerator/oss/saas-starter-kit",
|
||||
"email": "support@geeksinthewoods.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {},
|
||||
"securityDefinitions": {
|
||||
"ApiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
},
|
||||
"BasicAuth": {
|
||||
"type": "basic"
|
||||
},
|
||||
"OAuth2Password": {
|
||||
"type": "oauth2",
|
||||
"flow": "password",
|
||||
"tokenUrl": "https://example.com/v1/oauth/token"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
type swaggerInfo struct {
|
||||
Version string
|
||||
Host string
|
||||
BasePath string
|
||||
Title string
|
||||
Description string
|
||||
}
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo swaggerInfo
|
||||
|
||||
type s struct{}
|
||||
|
||||
func (s *s) ReadDoc() string {
|
||||
t, err := template.New("swagger_info").Parse(doc)
|
||||
if err != nil {
|
||||
return doc
|
||||
}
|
||||
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, SwaggerInfo); err != nil {
|
||||
return doc
|
||||
}
|
||||
|
||||
return tpl.String()
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(swag.Name, &s{})
|
||||
}
|
36
example-project/cmd/web-api/docs/swagger.json
Normal file
36
example-project/cmd/web-api/docs/swagger.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server celler server.",
|
||||
"title": "SaaS Example API",
|
||||
"termsOfService": "http://geeksinthewoods.com/terms",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "https://gitlab.com/geeks-accelerator/oss/saas-starter-kit",
|
||||
"email": "support@geeksinthewoods.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {},
|
||||
"securityDefinitions": {
|
||||
"ApiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
},
|
||||
"BasicAuth": {
|
||||
"type": "basic"
|
||||
},
|
||||
"OAuth2Password": {
|
||||
"type": "oauth2",
|
||||
"flow": "password",
|
||||
"tokenUrl": "https://example.com/v1/oauth/token"
|
||||
}
|
||||
}
|
||||
}
|
27
example-project/cmd/web-api/docs/swagger.yaml
Normal file
27
example-project/cmd/web-api/docs/swagger.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
basePath: '{{.BasePath}}'
|
||||
host: '{{.Host}}'
|
||||
info:
|
||||
contact:
|
||||
email: support@geeksinthewoods.com
|
||||
name: API Support
|
||||
url: https://gitlab.com/geeks-accelerator/oss/saas-starter-kit
|
||||
description: This is a sample server celler server.
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
termsOfService: http://geeksinthewoods.com/terms
|
||||
title: SaaS Example API
|
||||
version: '{{.Version}}'
|
||||
paths: {}
|
||||
securityDefinitions:
|
||||
ApiKeyAuth:
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
BasicAuth:
|
||||
type: basic
|
||||
OAuth2Password:
|
||||
flow: password
|
||||
tokenUrl: https://example.com/v1/oauth/token
|
||||
type: oauth2
|
||||
swagger: "2.0"
|
191
example-project/cmd/web-api/handlers/account.go
Normal file
191
example-project/cmd/web-api/handlers/account.go
Normal file
@ -0,0 +1,191 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/account"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Account represents the Account API method handler set.
|
||||
type Account struct {
|
||||
MasterDB *sqlx.DB
|
||||
|
||||
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
||||
}
|
||||
|
||||
// List returns all the existing accounts in the system.
|
||||
func (a *Account) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req account.AccountFindRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
res, err := account.Find(ctx, claims, a.MasterDB, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// Read returns the specified account from the system.
|
||||
func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var includeArchived bool
|
||||
if qv := r.URL.Query().Get("include-archived"); qv != "" {
|
||||
var err error
|
||||
includeArchived, err = strconv.ParseBool(qv)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Invalid value for include-archived : %s", qv)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := account.Read(ctx, claims, a.MasterDB, params["id"], includeArchived)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case account.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case account.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case account.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "ID: %s", params["id"])
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// Create inserts a new account into the system.
|
||||
func (a *Account) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req account.AccountCreateRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
res, err := account.Create(ctx, claims, a.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case account.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "User: %+v", &req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, res, http.StatusCreated)
|
||||
}
|
||||
|
||||
// Update updates the specified account in the system.
|
||||
func (a *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req account.AccountUpdateRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
req.ID = params["id"]
|
||||
|
||||
err := account.Update(ctx, claims, a.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case account.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case account.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case account.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s Account: %+v", params["id"], &req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Archive soft-deletes the specified account from the system.
|
||||
func (a *Account) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
err := account.Archive(ctx, claims, a.MasterDB, params["id"], v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case account.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case account.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case account.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s", params["id"])
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Delete removes the specified account from the system.
|
||||
func (a *Account) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
err := account.Delete(ctx, claims, a.MasterDB, params["id"])
|
||||
if err != nil {
|
||||
switch err {
|
||||
case account.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case account.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case account.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s", params["id"])
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
@ -6,20 +6,31 @@ import (
|
||||
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
||||
)
|
||||
|
||||
// Check provides support for orchestration health checks.
|
||||
type Check struct {
|
||||
MasterDB *sqlx.DB
|
||||
Redis *redis.Client
|
||||
|
||||
// ADD OTHER STATE LIKE THE LOGGER IF NEEDED.
|
||||
}
|
||||
|
||||
// Health validates the service is healthy and ready to accept requests.
|
||||
func (c *Check) Health(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
|
||||
// check postgres
|
||||
_, err := c.MasterDB.Exec("SELECT 1")
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "Postgres failed")
|
||||
}
|
||||
|
||||
// check redis
|
||||
err = c.Redis.Ping().Err()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Redis failed")
|
||||
}
|
||||
|
||||
status := struct {
|
||||
|
@ -3,7 +3,9 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/project"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@ -18,30 +20,56 @@ type Project struct {
|
||||
}
|
||||
|
||||
// List returns all the existing projects in the system.
|
||||
func (p *Project) List(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
projects, err := project.List(ctx, p.MasterDB)
|
||||
func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req project.ProjectFindRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
res, err := project.Find(ctx, claims, p.MasterDB, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, projects, http.StatusOK)
|
||||
return web.RespondJson(ctx, w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// Retrieve returns the specified project from the system.
|
||||
func (p *Project) Retrieve(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
prod, err := project.Retrieve(ctx, p.MasterDB, params["id"])
|
||||
// Read returns the specified project from the system.
|
||||
func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var includeArchived bool
|
||||
if qv := r.URL.Query().Get("include-archived"); qv != "" {
|
||||
var err error
|
||||
includeArchived, err = strconv.ParseBool(qv)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Invalid value for include-archived : %s", qv)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := project.Read(ctx, claims, p.MasterDB, params["id"], includeArchived)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case project.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case project.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case project.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "ID: %s", params["id"])
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, prod, http.StatusOK)
|
||||
return web.RespondJson(ctx, w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// Create inserts a new project into the system.
|
||||
@ -51,17 +79,27 @@ func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
var np project.NewProject
|
||||
if err := web.Decode(r, &np); err != nil {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req project.ProjectCreateRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
nUsr, err := project.Create(ctx, p.MasterDB, &np, v.Now)
|
||||
res, err := project.Create(ctx, claims, p.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Project: %+v", &np)
|
||||
switch err {
|
||||
case project.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Project: %+v", &req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nUsr, http.StatusCreated)
|
||||
return web.RespondJson(ctx, w, res, http.StatusCreated)
|
||||
}
|
||||
|
||||
// Update updates the specified project in the system.
|
||||
@ -71,20 +109,57 @@ func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
var up project.UpdateProject
|
||||
if err := web.Decode(r, &up); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
err := project.Update(ctx, p.MasterDB, params["id"], up, v.Now)
|
||||
var req project.ProjectUpdateRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
req.ID = params["id"]
|
||||
|
||||
err := project.Update(ctx, claims, p.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case project.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case project.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case project.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "ID: %s Update: %+v", params["id"], up)
|
||||
return errors.Wrapf(err, "ID: %s Update: %+v", params["id"], req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Archive soft-deletes the specified project from the system.
|
||||
func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
err := project.Archive(ctx, claims, p.MasterDB, params["id"], v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case project.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case project.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case project.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s", params["id"])
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,13 +168,20 @@ func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||
|
||||
// Delete removes the specified project from the system.
|
||||
func (p *Project) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
err := project.Delete(ctx, p.MasterDB, params["id"])
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
err := project.Delete(ctx, claims, p.MasterDB, params["id"])
|
||||
if err != nil {
|
||||
switch err {
|
||||
case project.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case project.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case project.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s", params["id"])
|
||||
}
|
||||
|
@ -6,13 +6,15 @@ import (
|
||||
"os"
|
||||
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/mid"
|
||||
saasSwagger "geeks-accelerator/oss/saas-starter-kit/example-project/internal/mid/saas-swagger"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
||||
)
|
||||
|
||||
// API returns a handler for a set of routes.
|
||||
func API(shutdown chan os.Signal, log *log.Logger, masterDB *sqlx.DB, authenticator *auth.Authenticator) http.Handler {
|
||||
func API(shutdown chan os.Signal, log *log.Logger, masterDB *sqlx.DB, redis *redis.Client, authenticator *auth.Authenticator) http.Handler {
|
||||
|
||||
// Construct the web.App which holds all routes as well as common Middleware.
|
||||
app := web.NewApp(shutdown, log, mid.Trace(), mid.Logger(log), mid.Errors(log), mid.Metrics(), mid.Panics())
|
||||
@ -28,24 +30,43 @@ func API(shutdown chan os.Signal, log *log.Logger, masterDB *sqlx.DB, authentica
|
||||
MasterDB: masterDB,
|
||||
TokenGenerator: authenticator,
|
||||
}
|
||||
app.Handle("GET", "/v1/users", u.List, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("GET", "/v1/users", u.Find, mid.Authenticate(authenticator))
|
||||
app.Handle("POST", "/v1/users", u.Create, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("GET", "/v1/users/:id", u.Retrieve, mid.Authenticate(authenticator))
|
||||
app.Handle("PUT", "/v1/users/:id", u.Update, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("GET", "/v1/users/:id", u.Read, mid.Authenticate(authenticator))
|
||||
app.Handle("PATCH", "/v1/users/:id", u.Update, mid.Authenticate(authenticator))
|
||||
app.Handle("PATCH", "/v1/users/:id/password", u.UpdatePassword, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("PATCH", "/v1/users/:id/archive", u.Archive, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("DELETE", "/v1/users/:id", u.Delete, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("PATCH", "/v1/users/switch-account/:accountId", u.SwitchAccount, mid.Authenticate(authenticator))
|
||||
|
||||
// This route is not authenticated
|
||||
app.Handle("GET", "/v1/users/token", u.Token)
|
||||
app.Handle("GET", "/v1/oauth/token", u.Token)
|
||||
|
||||
// Register project and sale endpoints.
|
||||
// Register account endpoints.
|
||||
a := Account{
|
||||
MasterDB: masterDB,
|
||||
}
|
||||
app.Handle("GET", "/v1/accounts", a.Find, mid.Authenticate(authenticator))
|
||||
app.Handle("POST", "/v1/accounts", a.Create, mid.Authenticate(authenticator))
|
||||
app.Handle("GET", "/v1/accounts/:id", a.Read, mid.Authenticate(authenticator))
|
||||
app.Handle("PATCH", "/v1/accounts/:id", a.Update, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("PATCH", "/v1/accounts/:id/archive", a.Archive, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("DELETE", "/v1/accounts/:id", a.Delete, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
|
||||
// Register project.
|
||||
p := Project{
|
||||
MasterDB: masterDB,
|
||||
}
|
||||
app.Handle("GET", "/v1/projects", p.List, mid.Authenticate(authenticator))
|
||||
app.Handle("POST", "/v1/projects", p.Create, mid.Authenticate(authenticator))
|
||||
app.Handle("GET", "/v1/projects/:id", p.Retrieve, mid.Authenticate(authenticator))
|
||||
app.Handle("PUT", "/v1/projects/:id", p.Update, mid.Authenticate(authenticator))
|
||||
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.Authenticate(authenticator))
|
||||
app.Handle("GET", "/v1/projects", p.Find, mid.Authenticate(authenticator))
|
||||
app.Handle("POST", "/v1/projects", p.Create, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("GET", "/v1/projects/:id", p.Read, mid.Authenticate(authenticator))
|
||||
app.Handle("PATCH", "/v1/projects/:id", p.Update, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("PATCH", "/v1/projects/:id/archive", p.Archive, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
|
||||
// Register swagger documentation.
|
||||
app.Handle("GET", "/swagger/", saasSwagger.WrapHandler, mid.Authenticate(authenticator))
|
||||
app.Handle("GET", "/swagger/*", saasSwagger.WrapHandler, mid.Authenticate(authenticator))
|
||||
|
||||
return app
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
|
||||
@ -12,6 +13,9 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// sessionTtl defines the auth token expiration.
|
||||
var sessionTtl = time.Hour * 24
|
||||
|
||||
// User represents the User API method handler set.
|
||||
type User struct {
|
||||
MasterDB *sqlx.DB
|
||||
@ -21,7 +25,7 @@ type User struct {
|
||||
}
|
||||
|
||||
// List returns all the existing users in the system.
|
||||
func (u *User) List(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
@ -32,22 +36,160 @@ func (u *User) List(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
usrs, err := user.Find(ctx, claims, u.MasterDB, req)
|
||||
res, err := user.Find(ctx, claims, u.MasterDB, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, usrs, http.StatusOK)
|
||||
return web.RespondJson(ctx, w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// Retrieve returns the specified user from the system.
|
||||
func (u *User) Retrieve(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
// Read returns the specified user from the system.
|
||||
func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
usr, err := user.FindById(ctx, claims, u.MasterDB, params["id"], false)
|
||||
var includeArchived bool
|
||||
if qv := r.URL.Query().Get("include-archived"); qv != "" {
|
||||
var err error
|
||||
includeArchived, err = strconv.ParseBool(qv)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Invalid value for include-archived : %s", qv)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := user.Read(ctx, claims, u.MasterDB, params["id"], includeArchived)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case user.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case user.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "ID: %s", params["id"])
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// Create inserts a new user into the system.
|
||||
func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req user.UserCreateRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
res, err := user.Create(ctx, claims, u.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "User: %+v", &req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, res, http.StatusCreated)
|
||||
}
|
||||
|
||||
// Update updates the specified user in the system.
|
||||
func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req user.UserUpdateRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
req.ID = params["id"]
|
||||
|
||||
err := user.Update(ctx, claims, u.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case user.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case user.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s User: %+v", params["id"], &req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Update updates the password for a specified user in the system.
|
||||
func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
var req user.UserUpdatePasswordRequest
|
||||
if err := web.Decode(r, &req); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
req.ID = params["id"]
|
||||
|
||||
err := user.UpdatePassword(ctx, claims, u.MasterDB, req, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case user.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case user.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s User: %+v", params["id"], &req)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Archive soft-deletes the specified user from the system.
|
||||
func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
err := user.Archive(ctx, claims, u.MasterDB, params["id"], v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrInvalidID:
|
||||
@ -61,66 +203,6 @@ func (u *User) Retrieve(ctx context.Context, w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, usr, http.StatusOK)
|
||||
}
|
||||
|
||||
// Create inserts a new user into the system.
|
||||
func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
var newU user.CreateUserRequest
|
||||
if err := web.Decode(r, &newU); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
usr, err := user.Create(ctx, claims, u.MasterDB, newU, v.Now)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "User: %+v", &usr)
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, usr, http.StatusCreated)
|
||||
}
|
||||
|
||||
// Update updates the specified user in the system.
|
||||
func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
var upd user.UpdateUserRequest
|
||||
if err := web.Decode(r, &upd); err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
err := user.Update(ctx, claims, u.MasterDB, upd, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrInvalidID:
|
||||
return web.NewRequestError(err, http.StatusBadRequest)
|
||||
case user.ErrNotFound:
|
||||
return web.NewRequestError(err, http.StatusNotFound)
|
||||
case user.ErrForbidden:
|
||||
return web.NewRequestError(err, http.StatusForbidden)
|
||||
default:
|
||||
return errors.Wrapf(err, "Id: %s User: %+v", params["id"], &upd)
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
@ -148,6 +230,31 @@ func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
||||
return web.RespondJson(ctx, w, nil, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// SwitchAccount updates the claims.
|
||||
func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
v, ok := ctx.Value(web.KeyValues).(*web.Values)
|
||||
if !ok {
|
||||
return web.NewShutdownError("web value missing from context")
|
||||
}
|
||||
|
||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||
if !ok {
|
||||
return errors.New("claims missing from context")
|
||||
}
|
||||
|
||||
tkn, err := user.SwitchAccount(ctx, u.MasterDB, u.TokenGenerator, claims, params["accountId"], sessionTtl, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrAuthenticationFailure:
|
||||
return web.NewRequestError(err, http.StatusUnauthorized)
|
||||
default:
|
||||
return errors.Wrap(err, "switch account")
|
||||
}
|
||||
}
|
||||
|
||||
return web.RespondJson(ctx, w, tkn, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Token handles a request to authenticate a user. It expects a request using
|
||||
// Basic Auth with a user's email and password. It responds with a JWT.
|
||||
func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||
@ -162,8 +269,7 @@ func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request
|
||||
return web.NewRequestError(err, http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// TODO Constant for token lifespan?
|
||||
tkn, err := user.Authenticate(ctx, u.MasterDB, u.TokenGenerator, email, pass, time.Hour * 24, v.Now)
|
||||
tkn, err := user.Authenticate(ctx, u.MasterDB, u.TokenGenerator, email, pass, sessionTtl, v.Now)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case user.ErrAuthenticationFailure:
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"expvar"
|
||||
"fmt"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
@ -16,6 +15,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/cmd/web-api/docs"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/cmd/web-api/handlers"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
|
||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/flag"
|
||||
@ -29,6 +29,7 @@ import (
|
||||
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
|
||||
redistrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
||||
sqlxtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/jmoiron/sqlx"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||
)
|
||||
|
||||
// build is the git version of this program. It is set using build flags in the makefile.
|
||||
@ -39,6 +40,26 @@ var build = "develop"
|
||||
// ie: export WEB_API_ENV=dev
|
||||
var service = "WEB_API"
|
||||
|
||||
// @title SaaS Example API
|
||||
// @description This is a sample server celler server.
|
||||
// @termsOfService http://geeksinthewoods.com/terms
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.email support@geeksinthewoods.com
|
||||
// @contact.url https://gitlab.com/geeks-accelerator/oss/saas-starter-kit
|
||||
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// @securityDefinitions.basic BasicAuth
|
||||
|
||||
// @securityDefinitions.apikey ApiKeyAuth
|
||||
// @in header
|
||||
// @name Authorization
|
||||
|
||||
// @securitydefinitions.oauth2.password OAuth2Password
|
||||
// @tokenUrl https://example.com/v1/oauth/token
|
||||
|
||||
func main() {
|
||||
|
||||
// =========================================================================
|
||||
@ -98,8 +119,9 @@ func main() {
|
||||
UseRole bool `envconfig:"AWS_USE_ROLE"`
|
||||
}
|
||||
Auth struct {
|
||||
AwsSecretID string `default:"auth-secret-key" envconfig:"AWS_SECRET_ID"`
|
||||
KeyExpiration time.Duration `default:"3600s" envconfig:"KEY_EXPIRATION"`
|
||||
UseAwsSecretManager bool `default:false envconfig:"USE_AWS_SECRET_MANAGER"`
|
||||
AwsSecretID string `default:"auth-secret-key" envconfig:"AWS_SECRET_ID"`
|
||||
KeyExpiration time.Duration `default:"3600s" envconfig:"KEY_EXPIRATION"`
|
||||
}
|
||||
BuildInfo struct {
|
||||
CiCommitRefName string `envconfig:"CI_COMMIT_REF_NAME"`
|
||||
@ -252,8 +274,13 @@ func main() {
|
||||
defer masterDb.Close()
|
||||
|
||||
// =========================================================================
|
||||
// Load auth keys from AWS and init new Authenticator
|
||||
authenticator, err := auth.NewAuthenticator(awsSession, cfg.Auth.AwsSecretID, time.Now().UTC(), cfg.Auth.KeyExpiration)
|
||||
// Init new Authenticator
|
||||
var authenticator *auth.Authenticator
|
||||
if cfg.Auth.UseAwsSecretManager {
|
||||
authenticator, err = auth.NewAuthenticatorAws(awsSession, cfg.Auth.AwsSecretID, time.Now().UTC(), cfg.Auth.KeyExpiration)
|
||||
} else {
|
||||
authenticator, err = auth.NewAuthenticatorFile("", time.Now().UTC(), cfg.Auth.KeyExpiration)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("main : Constructing authenticator : %v", err)
|
||||
}
|
||||
@ -282,6 +309,19 @@ func main() {
|
||||
// =========================================================================
|
||||
// Start API Service
|
||||
|
||||
// Programmatically set swagger info.
|
||||
{
|
||||
docs.SwaggerInfo.Version = build
|
||||
|
||||
u, err := url.Parse(cfg.App.BaseUrl)
|
||||
if err != nil {
|
||||
log.Fatalf("main : Parse app base url %s : %v", cfg.App.BaseUrl, err)
|
||||
}
|
||||
|
||||
docs.SwaggerInfo.Host = u.Host
|
||||
docs.SwaggerInfo.BasePath = "/v1"
|
||||
}
|
||||
|
||||
// Make a channel to listen for an interrupt or terminate signal from the OS.
|
||||
// Use a buffered channel because the signal package requires it.
|
||||
shutdown := make(chan os.Signal, 1)
|
||||
@ -289,7 +329,7 @@ func main() {
|
||||
|
||||
api := http.Server{
|
||||
Addr: cfg.HTTP.Host,
|
||||
Handler: handlers.API(shutdown, log, masterDb, authenticator),
|
||||
Handler: handlers.API(shutdown, log, masterDb, redisClient, authenticator),
|
||||
ReadTimeout: cfg.HTTP.ReadTimeout,
|
||||
WriteTimeout: cfg.HTTP.WriteTimeout,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
|
@ -30,6 +30,8 @@ func TestProjects(t *testing.T) {
|
||||
t.Run("crudProjects", crudProject)
|
||||
}
|
||||
|
||||
// TODO: need to test Archive
|
||||
|
||||
// getProjects200Empty validates an empty projects list can be retrieved with the endpoint.
|
||||
func getProjects200Empty(t *testing.T) {
|
||||
r := httptest.NewRequest("GET", "/v1/projects", nil)
|
||||
|
Reference in New Issue
Block a user