1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-08-08 22:36:41 +02:00

issue #28 - renamed project to checklist

This commit is contained in:
Lee Brown
2020-01-18 15:36:16 -09:00
parent 0027bef370
commit 35242354d5
26 changed files with 632 additions and 618 deletions

View File

@@ -220,7 +220,7 @@ var doc = `{
"application/json"
],
"tags": [
"project"
"checklist"
],
"summary": "List projects",
"parameters": [
@@ -261,7 +261,7 @@ var doc = `{
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/project.ProjectResponse"
"$ref": "#/definitions/checklist.ProjectResponse"
}
}
},
@@ -291,7 +291,7 @@ var doc = `{
"OAuth2Password": []
}
],
"description": "Create inserts a new project into the system.",
"description": "Create inserts a new checklist into the system.",
"consumes": [
"application/json"
],
@@ -299,9 +299,9 @@ var doc = `{
"application/json"
],
"tags": [
"project"
"checklist"
],
"summary": "Create new project.",
"summary": "Create new checklist.",
"parameters": [
{
"description": "Project details",
@@ -310,7 +310,7 @@ var doc = `{
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/project.ProjectCreateRequest"
"$ref": "#/definitions/checklist.ProjectCreateRequest"
}
}
],
@@ -318,7 +318,7 @@ var doc = `{
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/project.ProjectResponse"
"$ref": "#/definitions/checklist.ProjectResponse"
}
},
"400": {
@@ -353,7 +353,7 @@ var doc = `{
"OAuth2Password": []
}
],
"description": "Update updates the specified project in the system.",
"description": "Update updates the specified checklist in the system.",
"consumes": [
"application/json"
],
@@ -361,9 +361,9 @@ var doc = `{
"application/json"
],
"tags": [
"project"
"checklist"
],
"summary": "Update project by ID",
"summary": "Update checklist by ID",
"parameters": [
{
"description": "Update fields",
@@ -372,7 +372,7 @@ var doc = `{
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/project.ProjectUpdateRequest"
"$ref": "#/definitions/checklist.ProjectUpdateRequest"
}
}
],
@@ -406,7 +406,7 @@ var doc = `{
"OAuth2Password": []
}
],
"description": "Archive soft-deletes the specified project from the system.",
"description": "Archive soft-deletes the specified checklist from the system.",
"consumes": [
"application/json"
],
@@ -414,9 +414,9 @@ var doc = `{
"application/json"
],
"tags": [
"project"
"checklist"
],
"summary": "Archive project by ID",
"summary": "Archive checklist by ID",
"parameters": [
{
"description": "Update fields",
@@ -425,7 +425,7 @@ var doc = `{
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/project.ProjectArchiveRequest"
"$ref": "#/definitions/checklist.ProjectArchiveRequest"
}
}
],
@@ -459,7 +459,7 @@ var doc = `{
"OAuth2Password": []
}
],
"description": "Read returns the specified project from the system.",
"description": "Read returns the specified checklist from the system.",
"consumes": [
"application/json"
],
@@ -467,9 +467,9 @@ var doc = `{
"application/json"
],
"tags": [
"project"
"checklist"
],
"summary": "Get project by ID.",
"summary": "Get checklist by ID.",
"parameters": [
{
"type": "string",
@@ -483,7 +483,7 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/project.ProjectResponse"
"$ref": "#/definitions/checklist.ProjectResponse"
}
},
"400": {
@@ -512,7 +512,7 @@ var doc = `{
"OAuth2Password": []
}
],
"description": "Delete removes the specified project from the system.",
"description": "Delete removes the specified checklist from the system.",
"consumes": [
"application/json"
],
@@ -520,9 +520,9 @@ var doc = `{
"application/json"
],
"tags": [
"project"
"checklist"
],
"summary": "Delete project by ID",
"summary": "Delete checklist by ID",
"parameters": [
{
"type": "string",
@@ -1512,7 +1512,7 @@ var doc = `{
}
}
},
"project.ProjectArchiveRequest": {
"checklist.ProjectArchiveRequest": {
"type": "object",
"required": [
"id"
@@ -1524,7 +1524,7 @@ var doc = `{
}
}
},
"project.ProjectCreateRequest": {
"checklist.ProjectCreateRequest": {
"type": "object",
"required": [
"account_id",
@@ -1541,7 +1541,7 @@ var doc = `{
}
}
},
"project.ProjectResponse": {
"checklist.ProjectResponse": {
"type": "object",
"required": [
"account_id",
@@ -1579,7 +1579,7 @@ var doc = `{
}
}
},
"project.ProjectUpdateRequest": {
"checklist.ProjectUpdateRequest": {
"type": "object",
"required": [
"id"

View File

@@ -673,7 +673,7 @@ paths:
description: OK
schema:
items:
$ref: '#/definitions/project.ProjectResponse'
$ref: '#/definitions/checklist.ProjectResponse'
type: array
"400":
description: Bad Request
@@ -691,18 +691,18 @@ paths:
- OAuth2Password: []
summary: List projects
tags:
- project
- checklist
patch:
consumes:
- application/json
description: Update updates the specified project in the system.
description: Update updates the specified checklist in the system.
parameters:
- description: Update fields
in: body
name: data
required: true
schema:
$ref: '#/definitions/project.ProjectUpdateRequest'
$ref: '#/definitions/checklist.ProjectUpdateRequest'
type: object
produces:
- application/json
@@ -722,20 +722,20 @@ paths:
$ref: '#/definitions/weberror.ErrorResponse'
security:
- OAuth2Password: []
summary: Update project by ID
summary: Update checklist by ID
tags:
- project
- checklist
post:
consumes:
- application/json
description: Create inserts a new project into the system.
description: Create inserts a new checklist into the system.
parameters:
- description: Project details
in: body
name: data
required: true
schema:
$ref: '#/definitions/project.ProjectCreateRequest'
$ref: '#/definitions/checklist.ProjectCreateRequest'
type: object
produces:
- application/json
@@ -743,7 +743,7 @@ paths:
"201":
description: Created
schema:
$ref: '#/definitions/project.ProjectResponse'
$ref: '#/definitions/checklist.ProjectResponse'
"400":
description: Bad Request
schema:
@@ -762,14 +762,14 @@ paths:
$ref: '#/definitions/weberror.ErrorResponse'
security:
- OAuth2Password: []
summary: Create new project.
summary: Create new checklist.
tags:
- project
- checklist
/projects/{id}:
delete:
consumes:
- application/json
description: Delete removes the specified project from the system.
description: Delete removes the specified checklist from the system.
parameters:
- description: Project ID
in: path
@@ -794,13 +794,13 @@ paths:
$ref: '#/definitions/weberror.ErrorResponse'
security:
- OAuth2Password: []
summary: Delete project by ID
summary: Delete checklist by ID
tags:
- project
- checklist
get:
consumes:
- application/json
description: Read returns the specified project from the system.
description: Read returns the specified checklist from the system.
parameters:
- description: Project ID
in: path
@@ -813,7 +813,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/project.ProjectResponse'
$ref: '#/definitions/checklist.ProjectResponse'
"400":
description: Bad Request
schema:
@@ -828,21 +828,21 @@ paths:
$ref: '#/definitions/weberror.ErrorResponse'
security:
- OAuth2Password: []
summary: Get project by ID.
summary: Get checklist by ID.
tags:
- project
- checklist
/projects/archive:
patch:
consumes:
- application/json
description: Archive soft-deletes the specified project from the system.
description: Archive soft-deletes the specified checklist from the system.
parameters:
- description: Update fields
in: body
name: data
required: true
schema:
$ref: '#/definitions/project.ProjectArchiveRequest'
$ref: '#/definitions/checklist.ProjectArchiveRequest'
type: object
produces:
- application/json
@@ -862,9 +862,9 @@ paths:
$ref: '#/definitions/weberror.ErrorResponse'
security:
- OAuth2Password: []
summary: Archive project by ID
summary: Archive checklist by ID
tags:
- project
- checklist
/signup:
post:
consumes:

View File

@@ -6,28 +6,28 @@ import (
"strconv"
"strings"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/pkg/errors"
"gopkg.in/go-playground/validator.v9"
)
// Project represents the Project API method handler set.
type Projects struct {
Repository *project.Repository
// Checklist represents the Checklist API method handler set.
type Checklists struct {
Repository *checklist.Repository
// ADD OTHER STATE LIKE THE LOGGER IF NEEDED.
}
// Find godoc
// TODO: Need to implement unittests on projects/find endpoint. There are none.
// @Summary List projects
// @Description Find returns the existing projects in the system.
// @Tags project
// TODO: Need to implement unittests on checklists/find endpoint. There are none.
// @Summary List checklists
// @Description Find returns the existing checklists in the system.
// @Tags checklist
// @Accept json
// @Produce json
// @Security OAuth2Password
@@ -36,18 +36,18 @@ type Projects struct {
// @Param limit query integer false "Limit, example: 10"
// @Param offset query integer false "Offset, example: 20"
// @Param include-archived query boolean false "Included Archived, example: false"
// @Success 200 {array} project.ProjectResponse
// @Success 200 {array} checklist.ChecklistResponse
// @Failure 400 {object} weberror.ErrorResponse
// @Failure 403 {object} weberror.ErrorResponse
// @Failure 500 {object} weberror.ErrorResponse
// @Router /projects [get]
func (h *Projects) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// @Router /checklists [get]
func (h *Checklists) 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
var req checklist.ChecklistFindRequest
// Handle where query value if set.
if v := r.URL.Query().Get("where"); v != "" {
@@ -113,7 +113,7 @@ func (h *Projects) Find(ctx context.Context, w http.ResponseWriter, r *http.Requ
return err
}
var resp []*project.ProjectResponse
var resp []*checklist.ChecklistResponse
for _, m := range res {
resp = append(resp, m.Response(ctx))
}
@@ -122,19 +122,19 @@ func (h *Projects) Find(ctx context.Context, w http.ResponseWriter, r *http.Requ
}
// Read godoc
// @Summary Get project by ID.
// @Description Read returns the specified project from the system.
// @Tags project
// @Summary Get checklist by ID.
// @Description Read returns the specified checklist from the system.
// @Tags checklist
// @Accept json
// @Produce json
// @Security OAuth2Password
// @Param id path string true "Project ID"
// @Success 200 {object} project.ProjectResponse
// @Param id path string true "Checklist ID"
// @Success 200 {object} checklist.ChecklistResponse
// @Failure 400 {object} weberror.ErrorResponse
// @Failure 404 {object} weberror.ErrorResponse
// @Failure 500 {object} weberror.ErrorResponse
// @Router /projects/{id} [get]
func (h *Projects) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// @Router /checklists/{id} [get]
func (h *Checklists) 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")
@@ -151,14 +151,14 @@ func (h *Projects) Read(ctx context.Context, w http.ResponseWriter, r *http.Requ
includeArchived = b
}
res, err := h.Repository.Read(ctx, claims, project.ProjectReadRequest{
res, err := h.Repository.Read(ctx, claims, checklist.ChecklistReadRequest{
ID: params["id"],
IncludeArchived: includeArchived,
})
if err != nil {
cause := errors.Cause(err)
switch cause {
case project.ErrNotFound:
case checklist.ErrNotFound:
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusNotFound))
default:
return errors.Wrapf(err, "ID: %s", params["id"])
@@ -169,20 +169,20 @@ func (h *Projects) Read(ctx context.Context, w http.ResponseWriter, r *http.Requ
}
// Create godoc
// @Summary Create new project.
// @Description Create inserts a new project into the system.
// @Tags project
// @Summary Create new checklist.
// @Description Create inserts a new checklist into the system.
// @Tags checklist
// @Accept json
// @Produce json
// @Security OAuth2Password
// @Param data body project.ProjectCreateRequest true "Project details"
// @Success 201 {object} project.ProjectResponse
// @Param data body checklist.ChecklistCreateRequest true "Checklist details"
// @Success 201 {object} checklist.ChecklistResponse
// @Failure 400 {object} weberror.ErrorResponse
// @Failure 403 {object} weberror.ErrorResponse
// @Failure 404 {object} weberror.ErrorResponse
// @Failure 500 {object} weberror.ErrorResponse
// @Router /projects [post]
func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// @Router /checklists [post]
func (h *Checklists) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, err := webcontext.ContextValues(ctx)
if err != nil {
return err
@@ -193,7 +193,7 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
return err
}
var req project.ProjectCreateRequest
var req checklist.ChecklistCreateRequest
if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = weberror.NewError(ctx, err, http.StatusBadRequest)
@@ -205,14 +205,14 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
if err != nil {
cause := errors.Cause(err)
switch cause {
case project.ErrForbidden:
case checklist.ErrForbidden:
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default:
_, ok := cause.(validator.ValidationErrors)
if ok {
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
}
return errors.Wrapf(err, "Project: %+v", &req)
return errors.Wrapf(err, "Checklist: %+v", &req)
}
}
@@ -220,19 +220,19 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
}
// Read godoc
// @Summary Update project by ID
// @Description Update updates the specified project in the system.
// @Tags project
// @Summary Update checklist by ID
// @Description Update updates the specified checklist in the system.
// @Tags checklist
// @Accept json
// @Produce json
// @Security OAuth2Password
// @Param data body project.ProjectUpdateRequest true "Update fields"
// @Param data body checklist.ChecklistUpdateRequest true "Update fields"
// @Success 204
// @Failure 400 {object} weberror.ErrorResponse
// @Failure 403 {object} weberror.ErrorResponse
// @Failure 500 {object} weberror.ErrorResponse
// @Router /projects [patch]
func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// @Router /checklists [patch]
func (h *Checklists) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, err := webcontext.ContextValues(ctx)
if err != nil {
return err
@@ -243,7 +243,7 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
return err
}
var req project.ProjectUpdateRequest
var req checklist.ChecklistUpdateRequest
if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = weberror.NewError(ctx, err, http.StatusBadRequest)
@@ -255,7 +255,7 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
if err != nil {
cause := errors.Cause(err)
switch cause {
case project.ErrForbidden:
case checklist.ErrForbidden:
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default:
_, ok := cause.(validator.ValidationErrors)
@@ -271,19 +271,19 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
}
// Read godoc
// @Summary Archive project by ID
// @Description Archive soft-deletes the specified project from the system.
// @Tags project
// @Summary Archive checklist by ID
// @Description Archive soft-deletes the specified checklist from the system.
// @Tags checklist
// @Accept json
// @Produce json
// @Security OAuth2Password
// @Param data body project.ProjectArchiveRequest true "Update fields"
// @Param data body checklist.ChecklistArchiveRequest true "Update fields"
// @Success 204
// @Failure 400 {object} weberror.ErrorResponse
// @Failure 403 {object} weberror.ErrorResponse
// @Failure 500 {object} weberror.ErrorResponse
// @Router /projects/archive [patch]
func (h *Projects) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// @Router /checklists/archive [patch]
func (h *Checklists) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, err := webcontext.ContextValues(ctx)
if err != nil {
return err
@@ -294,7 +294,7 @@ func (h *Projects) Archive(ctx context.Context, w http.ResponseWriter, r *http.R
return err
}
var req project.ProjectArchiveRequest
var req checklist.ChecklistArchiveRequest
if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = weberror.NewError(ctx, err, http.StatusBadRequest)
@@ -306,7 +306,7 @@ func (h *Projects) Archive(ctx context.Context, w http.ResponseWriter, r *http.R
if err != nil {
cause := errors.Cause(err)
switch cause {
case project.ErrForbidden:
case checklist.ErrForbidden:
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default:
_, ok := cause.(validator.ValidationErrors)
@@ -322,30 +322,30 @@ func (h *Projects) Archive(ctx context.Context, w http.ResponseWriter, r *http.R
}
// Delete godoc
// @Summary Delete project by ID
// @Description Delete removes the specified project from the system.
// @Tags project
// @Summary Delete checklist by ID
// @Description Delete removes the specified checklist from the system.
// @Tags checklist
// @Accept json
// @Produce json
// @Security OAuth2Password
// @Param id path string true "Project ID"
// @Param id path string true "Checklist ID"
// @Success 204
// @Failure 400 {object} weberror.ErrorResponse
// @Failure 403 {object} weberror.ErrorResponse
// @Failure 500 {object} weberror.ErrorResponse
// @Router /projects/{id} [delete]
func (h *Projects) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// @Router /checklists/{id} [delete]
func (h *Checklists) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
claims, err := auth.ClaimsFromContext(ctx)
if err != nil {
return err
}
err = h.Repository.Delete(ctx, claims,
project.ProjectDeleteRequest{ID: params["id"]})
checklist.ChecklistDeleteRequest{ID: params["id"]})
if err != nil {
cause := errors.Cause(err)
switch cause {
case project.ErrForbidden:
case checklist.ErrForbidden:
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default:
_, ok := cause.(validator.ValidationErrors)

View File

@@ -4,18 +4,18 @@ import (
"context"
"net/http"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/pkg/errors"
)
// Example represents the Example API method handler set.
type Example struct {
Project *project.Repository
Checklist *checklist.Repository
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
}
@@ -28,7 +28,7 @@ func (h *Example) ErrorResponse(ctx context.Context, w http.ResponseWriter, r *h
}
if qv := r.URL.Query().Get("test-validation-error"); qv != "" {
_, err := h.Project.Create(ctx, auth.Claims{}, project.ProjectCreateRequest{}, v.Now)
_, err := h.Checklist.Create(ctx, auth.Claims{}, checklist.ChecklistCreateRequest{}, v.Now)
return web.RespondJsonError(ctx, w, err)
}

View File

@@ -7,13 +7,13 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
saasSwagger "geeks-accelerator/oss/saas-starter-kit/internal/mid/saas-swagger"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
_ "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
@@ -36,7 +36,7 @@ type AppContext struct {
AuthRepo *user_auth.Repository
SignupRepo *signup.Repository
InviteRepo *invite.Repository
ProjectRepo *project.Repository
ChecklistRepo *checklist.Repository
Authenticator *auth.Authenticator
PreAppMiddleware []web.Middleware
PostAppMiddleware []web.Middleware
@@ -74,7 +74,7 @@ func API(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
// Register example endpoints.
ex := Example{
Project: appCtx.ProjectRepo,
Checklist: appCtx.ChecklistRepo,
}
app.Handle("GET", "/v1/examples/error-response", ex.ErrorResponse)
@@ -119,16 +119,16 @@ func API(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
}
app.Handle("POST", "/v1/signup", s.Signup)
// Register project.
p := Projects{
Repository: appCtx.ProjectRepo,
// Register checklist.
p := Checklists{
Repository: appCtx.ChecklistRepo,
}
app.Handle("GET", "/v1/projects", p.Find, mid.AuthenticateHeader(appCtx.Authenticator))
app.Handle("POST", "/v1/projects", p.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/v1/projects/:id", p.Read, mid.AuthenticateHeader(appCtx.Authenticator))
app.Handle("PATCH", "/v1/projects", p.Update, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("PATCH", "/v1/projects/archive", p.Archive, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/v1/checklists", p.Find, mid.AuthenticateHeader(appCtx.Authenticator))
app.Handle("POST", "/v1/checklists", p.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/v1/checklists/:id", p.Read, mid.AuthenticateHeader(appCtx.Authenticator))
app.Handle("PATCH", "/v1/checklists", p.Update, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("PATCH", "/v1/checklists/archive", p.Archive, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("DELETE", "/v1/checklists/:id", p.Delete, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
// Register swagger documentation.
// TODO: Add authentication. Current authenticator requires an Authorization header

View File

@@ -22,18 +22,18 @@ import (
"geeks-accelerator/oss/saas-starter-kit/cmd/web-api/handlers"
"geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/flag"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/notify"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
@@ -437,19 +437,19 @@ func main() {
// =========================================================================
// Init repositories and AppContext
projectRoute, err := webroute.New(cfg.Service.BaseUrl, cfg.Project.WebAppBaseUrl)
webRoute, err := webroute.New(cfg.Service.BaseUrl, cfg.Project.WebAppBaseUrl)
if err != nil {
log.Fatalf("main : project routes : %s: %+v", cfg.Service.BaseUrl, err)
log.Fatalf("main : checklist routes : %s: %+v", cfg.Service.BaseUrl, err)
}
usrRepo := user.NewRepository(masterDb, projectRoute.UserResetPassword, notifyEmail, cfg.Project.SharedSecretKey)
usrRepo := user.NewRepository(masterDb, webRoute.UserResetPassword, notifyEmail, cfg.Project.SharedSecretKey)
usrAccRepo := user_account.NewRepository(masterDb)
accRepo := account.NewRepository(masterDb)
accPrefRepo := account_preference.NewRepository(masterDb)
authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo)
signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo)
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
prjRepo := project.NewRepository(masterDb)
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, webRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
chklstRepo := checklist.NewRepository(masterDb)
appCtx := &handlers.AppContext{
Log: log,
@@ -463,7 +463,7 @@ func main() {
AuthRepo: authRepo,
SignupRepo: signupRepo,
InviteRepo: inviteRepo,
ProjectRepo: prjRepo,
ChecklistRepo: chklstRepo,
Authenticator: authenticator,
}

View File

@@ -8,26 +8,26 @@ import (
"testing"
"time"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/pborman/uuid"
)
func mockProjectCreateRequest(accountID string) project.ProjectCreateRequest {
return project.ProjectCreateRequest{
func mockChecklistCreateRequest(accountID string) checklist.ChecklistCreateRequest {
return checklist.ChecklistCreateRequest{
Name: fmt.Sprintf("Moon Launch %s", uuid.NewRandom().String()),
AccountID: accountID,
}
}
// mockProject creates a new project for testing and associates it with the supplied account ID.
func newMockProject(accountID string) *project.Project {
req := mockProjectCreateRequest(accountID)
p, err := appCtx.ProjectRepo.Create(tests.Context(), auth.Claims{}, req, time.Now().UTC().AddDate(-1, -1, -1))
// mockChecklist creates a new checklist for testing and associates it with the supplied account ID.
func newMockChecklist(accountID string) *checklist.Checklist {
req := mockChecklistCreateRequest(accountID)
p, err := appCtx.ChecklistRepo.Create(tests.Context(), auth.Claims{}, req, time.Now().UTC().AddDate(-1, -1, -1))
if err != nil {
panic(err)
}
@@ -35,25 +35,25 @@ func newMockProject(accountID string) *project.Project {
return p
}
// TestProjectCRUDAdmin tests all the project CRUD endpoints using an user with role admin.
func TestProjectCRUDAdmin(t *testing.T) {
// TestChecklistCRUDAdmin tests all the checklist CRUD endpoints using an user with role admin.
func TestChecklistCRUDAdmin(t *testing.T) {
defer tests.Recover(t)
tr := roleTests[auth.RoleAdmin]
// Add claims to the context for the project.
// Add claims to the context for the checklist.
ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)
// Test create.
var created project.ProjectResponse
var created checklist.ChecklistResponse
{
expectedStatus := http.StatusCreated
req := mockProjectCreateRequest(tr.Account.ID)
req := mockChecklistCreateRequest(tr.Account.ID)
rt := requestTest{
fmt.Sprintf("Create %d w/role %s", expectedStatus, tr.Role),
http.MethodPost,
"/v1/projects",
"/v1/checklists",
req,
tr.Token,
tr.Claims,
@@ -68,7 +68,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
}
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual project.ProjectResponse
var actual checklist.ChecklistResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
@@ -79,12 +79,12 @@ func TestProjectCRUDAdmin(t *testing.T) {
"updated_at": web.NewTimeResponse(ctx, actual.UpdatedAt.Value),
"id": actual.ID,
"account_id": req.AccountID,
"status": web.NewEnumResponse(ctx, "active", project.ProjectStatus_ValuesInterface()...),
"status": web.NewEnumResponse(ctx, "active", checklist.ChecklistStatus_ValuesInterface()...),
"created_at": web.NewTimeResponse(ctx, actual.CreatedAt.Value),
"name": req.Name,
}
var expected project.ProjectResponse
var expected checklist.ChecklistResponse
if err := decodeMapToStruct(expectedMap, &expected); err != nil {
t.Logf("\t\tGot error : %+v\nActual results to format expected : \n", err)
printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap
@@ -107,7 +107,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Read %d w/role %s", expectedStatus, tr.Role),
http.MethodGet,
fmt.Sprintf("/v1/projects/%s", created.ID),
fmt.Sprintf("/v1/checklists/%s", created.ID),
nil,
tr.Token,
tr.Claims,
@@ -122,7 +122,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
}
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual project.ProjectResponse
var actual checklist.ChecklistResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
@@ -142,7 +142,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Read %d w/role %s using random ID", expectedStatus, tr.Role),
http.MethodGet,
fmt.Sprintf("/v1/projects/%s", randID),
fmt.Sprintf("/v1/checklists/%s", randID),
nil,
tr.Token,
tr.Claims,
@@ -166,7 +166,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
expected := weberror.ErrorResponse{
StatusCode: expectedStatus,
Error: http.StatusText(expectedStatus),
Details: fmt.Sprintf("project %s not found: Entity not found", randID),
Details: fmt.Sprintf("checklist %s not found: Entity not found", randID),
StackTrace: actual.StackTrace,
}
@@ -177,14 +177,14 @@ func TestProjectCRUDAdmin(t *testing.T) {
}
// Test Read with forbidden ID.
forbiddenProject := newMockProject(newMockSignup().account.ID)
forbiddenChecklist := newMockChecklist(newMockSignup().account.ID)
{
expectedStatus := http.StatusNotFound
rt := requestTest{
fmt.Sprintf("Read %d w/role %s using forbidden ID", expectedStatus, tr.Role),
http.MethodGet,
fmt.Sprintf("/v1/projects/%s", forbiddenProject.ID),
fmt.Sprintf("/v1/checklists/%s", forbiddenChecklist.ID),
nil,
tr.Token,
tr.Claims,
@@ -208,7 +208,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
expected := weberror.ErrorResponse{
StatusCode: expectedStatus,
Error: http.StatusText(expectedStatus),
Details: fmt.Sprintf("project %s not found: Entity not found", forbiddenProject.ID),
Details: fmt.Sprintf("checklist %s not found: Entity not found", forbiddenChecklist.ID),
StackTrace: actual.StackTrace,
}
@@ -226,8 +226,8 @@ func TestProjectCRUDAdmin(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Update %d w/role %s", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects",
project.ProjectUpdateRequest{
"/v1/checklists",
checklist.ChecklistUpdateRequest{
ID: created.ID,
Name: &newName,
},
@@ -259,8 +259,8 @@ func TestProjectCRUDAdmin(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Archive %d w/role %s", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects/archive",
project.ProjectArchiveRequest{
"/v1/checklists/archive",
checklist.ChecklistArchiveRequest{
ID: created.ID,
},
tr.Token,
@@ -291,7 +291,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Delete %d w/role %s", expectedStatus, tr.Role),
http.MethodDelete,
fmt.Sprintf("/v1/projects/%s", created.ID),
fmt.Sprintf("/v1/checklists/%s", created.ID),
nil,
tr.Token,
tr.Claims,
@@ -315,24 +315,24 @@ func TestProjectCRUDAdmin(t *testing.T) {
}
}
// TestProjectCRUDUser tests all the project CRUD endpoints using an user with role project.
func TestProjectCRUDUser(t *testing.T) {
// TestChecklistCRUDUser tests all the checklist CRUD endpoints using an user with role checklist.
func TestChecklistCRUDUser(t *testing.T) {
defer tests.Recover(t)
tr := roleTests[auth.RoleUser]
// Add claims to the context for the project.
// Add claims to the context for the checklist.
ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)
// Test create.
{
expectedStatus := http.StatusForbidden
req := mockProjectCreateRequest(tr.Account.ID)
req := mockChecklistCreateRequest(tr.Account.ID)
rt := requestTest{
fmt.Sprintf("Create %d w/role %s", expectedStatus, tr.Role),
http.MethodPost,
"/v1/projects",
"/v1/checklists",
req,
tr.Token,
tr.Claims,
@@ -363,7 +363,7 @@ func TestProjectCRUDUser(t *testing.T) {
}
// Since role doesn't support create, bypass auth to test other endpoints.
created := newMockProject(tr.Account.ID).Response(ctx)
created := newMockChecklist(tr.Account.ID).Response(ctx)
// Test read.
{
@@ -372,7 +372,7 @@ func TestProjectCRUDUser(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Read %d w/role %s", expectedStatus, tr.Role),
http.MethodGet,
fmt.Sprintf("/v1/projects/%s", created.ID),
fmt.Sprintf("/v1/checklists/%s", created.ID),
nil,
tr.Token,
tr.Claims,
@@ -387,7 +387,7 @@ func TestProjectCRUDUser(t *testing.T) {
}
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual *project.ProjectResponse
var actual *checklist.ChecklistResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
@@ -407,7 +407,7 @@ func TestProjectCRUDUser(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Read %d w/role %s using random ID", expectedStatus, tr.Role),
http.MethodGet,
fmt.Sprintf("/v1/projects/%s", randID),
fmt.Sprintf("/v1/checklists/%s", randID),
nil,
tr.Token,
tr.Claims,
@@ -431,7 +431,7 @@ func TestProjectCRUDUser(t *testing.T) {
expected := weberror.ErrorResponse{
StatusCode: expectedStatus,
Error: http.StatusText(expectedStatus),
Details: fmt.Sprintf("project %s not found: Entity not found", randID),
Details: fmt.Sprintf("checklist %s not found: Entity not found", randID),
StackTrace: actual.StackTrace,
}
@@ -442,14 +442,14 @@ func TestProjectCRUDUser(t *testing.T) {
}
// Test Read with forbidden ID.
forbiddenProject := newMockProject(newMockSignup().account.ID)
forbiddenChecklist := newMockChecklist(newMockSignup().account.ID)
{
expectedStatus := http.StatusNotFound
rt := requestTest{
fmt.Sprintf("Read %d w/role %s using forbidden ID", expectedStatus, tr.Role),
http.MethodGet,
fmt.Sprintf("/v1/projects/%s", forbiddenProject.ID),
fmt.Sprintf("/v1/checklists/%s", forbiddenChecklist.ID),
nil,
tr.Token,
tr.Claims,
@@ -473,7 +473,7 @@ func TestProjectCRUDUser(t *testing.T) {
expected := weberror.ErrorResponse{
StatusCode: expectedStatus,
Error: http.StatusText(expectedStatus),
Details: fmt.Sprintf("project %s not found: Entity not found", forbiddenProject.ID),
Details: fmt.Sprintf("checklist %s not found: Entity not found", forbiddenChecklist.ID),
StackTrace: actual.StackTrace,
}
@@ -491,8 +491,8 @@ func TestProjectCRUDUser(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Update %d w/role %s", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects",
project.ProjectUpdateRequest{
"/v1/checklists",
checklist.ChecklistUpdateRequest{
ID: created.ID,
Name: &newName,
},
@@ -531,8 +531,8 @@ func TestProjectCRUDUser(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Archive %d w/role %s", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects/archive",
project.ProjectArchiveRequest{
"/v1/checklists/archive",
checklist.ChecklistArchiveRequest{
ID: created.ID,
},
tr.Token,
@@ -570,7 +570,7 @@ func TestProjectCRUDUser(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Delete %d w/role %s", expectedStatus, tr.Role),
http.MethodDelete,
fmt.Sprintf("/v1/projects/%s", created.ID),
fmt.Sprintf("/v1/checklists/%s", created.ID),
nil,
tr.Token,
tr.Claims,
@@ -601,26 +601,26 @@ func TestProjectCRUDUser(t *testing.T) {
}
}
// TestProjectCreate validates create project endpoint.
func TestProjectCreate(t *testing.T) {
// TestChecklistCreate validates create checklist endpoint.
func TestChecklistCreate(t *testing.T) {
defer tests.Recover(t)
tr := roleTests[auth.RoleAdmin]
// Add claims to the context for the project.
// Add claims to the context for the checklist.
ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)
// Test create with invalid data.
{
expectedStatus := http.StatusBadRequest
req := mockProjectCreateRequest(tr.Account.ID)
invalidStatus := project.ProjectStatus("invalid status")
req := mockChecklistCreateRequest(tr.Account.ID)
invalidStatus := checklist.ChecklistStatus("invalid status")
req.Status = &invalidStatus
rt := requestTest{
fmt.Sprintf("Create %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPost,
"/v1/projects",
"/v1/checklists",
req,
tr.Token,
tr.Claims,
@@ -646,7 +646,7 @@ func TestProjectCreate(t *testing.T) {
Details: actual.Details,
Error: "Field validation error",
Fields: []weberror.FieldError{
//{Field: "status", Error: "Key: 'ProjectCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
//{Field: "status", Error: "Key: 'ChecklistCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
@@ -665,25 +665,25 @@ func TestProjectCreate(t *testing.T) {
}
}
// TestProjectUpdate validates update project endpoint.
func TestProjectUpdate(t *testing.T) {
// TestChecklistUpdate validates update checklist endpoint.
func TestChecklistUpdate(t *testing.T) {
defer tests.Recover(t)
tr := roleTests[auth.RoleAdmin]
// Add claims to the context for the project.
// Add claims to the context for the checklist.
ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)
// Test update with invalid data.
{
expectedStatus := http.StatusBadRequest
invalidStatus := project.ProjectStatus("invalid status")
invalidStatus := checklist.ChecklistStatus("invalid status")
rt := requestTest{
fmt.Sprintf("Update %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects",
project.ProjectUpdateRequest{
"/v1/checklists",
checklist.ChecklistUpdateRequest{
ID: uuid.NewRandom().String(),
Status: &invalidStatus,
},
@@ -711,7 +711,7 @@ func TestProjectUpdate(t *testing.T) {
Details: actual.Details,
Error: "Field validation error",
Fields: []weberror.FieldError{
//{Field: "status", Error: "Key: 'ProjectUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
//{Field: "status", Error: "Key: 'ChecklistUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
@@ -730,16 +730,16 @@ func TestProjectUpdate(t *testing.T) {
}
}
// TestProjectArchive validates archive project endpoint.
func TestProjectArchive(t *testing.T) {
// TestChecklistArchive validates archive checklist endpoint.
func TestChecklistArchive(t *testing.T) {
defer tests.Recover(t)
tr := roleTests[auth.RoleAdmin]
// Add claims to the context for the project.
// Add claims to the context for the checklist.
ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)
forbiddenProject := newMockProject(newMockSignup().account.ID)
forbiddenChecklist := newMockChecklist(newMockSignup().account.ID)
// Test archive with invalid data.
{
@@ -750,8 +750,8 @@ func TestProjectArchive(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects/archive",
project.ProjectArchiveRequest{
"/v1/checklists/archive",
checklist.ChecklistArchiveRequest{
ID: invalidId,
},
tr.Token,
@@ -778,7 +778,7 @@ func TestProjectArchive(t *testing.T) {
Details: actual.Details,
Error: "Field validation error",
Fields: []weberror.FieldError{
//{Field: "id", Error: "Key: 'ProjectArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"},
//{Field: "id", Error: "Key: 'ChecklistArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"},
{
Field: "id",
Value: invalidId,
@@ -803,9 +803,9 @@ func TestProjectArchive(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Archive %d w/role %s using forbidden ID", expectedStatus, tr.Role),
http.MethodPatch,
"/v1/projects/archive",
project.ProjectArchiveRequest{
ID: forbiddenProject.ID,
"/v1/checklists/archive",
checklist.ChecklistArchiveRequest{
ID: forbiddenChecklist.ID,
},
tr.Token,
tr.Claims,
@@ -829,7 +829,7 @@ func TestProjectArchive(t *testing.T) {
expected := weberror.ErrorResponse{
StatusCode: expectedStatus,
Error: http.StatusText(expectedStatus),
Details: project.ErrForbidden.Error(),
Details: checklist.ErrForbidden.Error(),
StackTrace: actual.StackTrace,
}
@@ -840,16 +840,16 @@ func TestProjectArchive(t *testing.T) {
}
}
// TestProjectDelete validates delete project endpoint.
func TestProjectDelete(t *testing.T) {
// TestChecklistDelete validates delete checklist endpoint.
func TestChecklistDelete(t *testing.T) {
defer tests.Recover(t)
tr := roleTests[auth.RoleAdmin]
// Add claims to the context for the project.
// Add claims to the context for the checklist.
ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)
forbiddenProject := newMockProject(newMockSignup().account.ID)
forbiddenChecklist := newMockChecklist(newMockSignup().account.ID)
// Test delete with invalid data.
{
@@ -860,7 +860,7 @@ func TestProjectDelete(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodDelete,
"/v1/projects/" + invalidId,
"/v1/checklists/" + invalidId,
nil,
tr.Token,
tr.Claims,
@@ -911,7 +911,7 @@ func TestProjectDelete(t *testing.T) {
rt := requestTest{
fmt.Sprintf("Delete %d w/role %s using forbidden ID", expectedStatus, tr.Role),
http.MethodDelete,
fmt.Sprintf("/v1/projects/%s", forbiddenProject.ID),
fmt.Sprintf("/v1/checklists/%s", forbiddenChecklist.ID),
nil,
tr.Token,
tr.Claims,
@@ -935,7 +935,7 @@ func TestProjectDelete(t *testing.T) {
expected := weberror.ErrorResponse{
StatusCode: expectedStatus,
Error: http.StatusText(expectedStatus),
Details: project.ErrForbidden.Error(),
Details: checklist.ErrForbidden.Error(),
StackTrace: actual.StackTrace,
}

View File

@@ -17,19 +17,19 @@ import (
"geeks-accelerator/oss/saas-starter-kit/cmd/web-api/handlers"
"geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/notify"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/google/go-cmp/cmp"
"github.com/iancoleman/strcase"
"github.com/pborman/uuid"
@@ -107,7 +107,7 @@ func testMain(m *testing.M) int {
authRepo := user_auth.NewRepository(test.MasterDB, authenticator, usrRepo, usrAccRepo, accPrefRepo)
signupRepo := signup.NewRepository(test.MasterDB, usrRepo, usrAccRepo, accRepo)
inviteRepo := invite.NewRepository(test.MasterDB, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, "6368616e676520746869732070613434")
prjRepo := project.NewRepository(test.MasterDB)
prjRepo := checklist.NewRepository(test.MasterDB)
appCtx = &handlers.AppContext{
Log: log,

View File

@@ -6,50 +6,50 @@ import (
"net/http"
"strings"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/datatable"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
)
// Projects represents the Projects API method handler set.
type Projects struct {
ProjectRepo *project.Repository
Redis *redis.Client
Renderer web.Renderer
// Checklists represents the Checklists API method handler set.
type Checklists struct {
ChecklistRepo *checklist.Repository
Redis *redis.Client
Renderer web.Renderer
}
func urlProjectsIndex() string {
return fmt.Sprintf("/projects")
func urlChecklistsIndex() string {
return fmt.Sprintf("/checklists")
}
func urlProjectsCreate() string {
return fmt.Sprintf("/projects/create")
func urlChecklistsCreate() string {
return fmt.Sprintf("/checklists/create")
}
func urlProjectsView(projectID string) string {
return fmt.Sprintf("/projects/%s", projectID)
func urlChecklistsView(checklistID string) string {
return fmt.Sprintf("/checklists/%s", checklistID)
}
func urlProjectsUpdate(projectID string) string {
return fmt.Sprintf("/projects/%s/update", projectID)
func urlChecklistsUpdate(checklistID string) string {
return fmt.Sprintf("/checklists/%s/update", checklistID)
}
// Index handles listing all the projects for the current account.
func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// Index handles listing all the checklists for the current account.
func (h *Checklists) Index(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
claims, err := auth.ClaimsFromContext(ctx)
if err != nil {
return err
}
statusOpts := web.NewEnumResponse(ctx, nil, project.ProjectStatus_ValuesInterface()...)
statusOpts := web.NewEnumResponse(ctx, nil, checklist.ChecklistStatus_ValuesInterface()...)
statusFilterItems := []datatable.FilterOptionItem{}
for _, opt := range statusOpts.Options {
@@ -61,13 +61,13 @@ func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Req
fields := []datatable.DisplayField{
datatable.DisplayField{Field: "id", Title: "ID", Visible: false, Searchable: true, Orderable: true, Filterable: false},
datatable.DisplayField{Field: "name", Title: "Project", Visible: true, Searchable: true, Orderable: true, Filterable: true, FilterPlaceholder: "filter Name"},
datatable.DisplayField{Field: "name", Title: "Checklist", Visible: true, Searchable: true, Orderable: true, Filterable: true, FilterPlaceholder: "filter Name"},
datatable.DisplayField{Field: "status", Title: "Status", Visible: true, Searchable: true, Orderable: true, Filterable: true, FilterPlaceholder: "All Statuses", FilterItems: statusFilterItems},
datatable.DisplayField{Field: "updated_at", Title: "Last Updated", Visible: true, Searchable: true, Orderable: true, Filterable: false},
datatable.DisplayField{Field: "created_at", Title: "Created", Visible: true, Searchable: true, Orderable: true, Filterable: false},
}
mapFunc := func(q *project.Project, cols []datatable.DisplayField) (resp []datatable.ColumnValue, err error) {
mapFunc := func(q *checklist.Checklist, cols []datatable.DisplayField) (resp []datatable.ColumnValue, err error) {
for i := 0; i < len(cols); i++ {
col := cols[i]
var v datatable.ColumnValue
@@ -76,17 +76,17 @@ func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Req
v.Value = fmt.Sprintf("%s", q.ID)
case "name":
v.Value = q.Name
v.Formatted = fmt.Sprintf("<a href='%s'>%s</a>", urlProjectsView(q.ID), v.Value)
v.Formatted = fmt.Sprintf("<a href='%s'>%s</a>", urlChecklistsView(q.ID), v.Value)
case "status":
v.Value = q.Status.String()
var subStatusClass string
var subStatusIcon string
switch q.Status {
case project.ProjectStatus_Active:
case checklist.ChecklistStatus_Active:
subStatusClass = "text-green"
subStatusIcon = "far fa-dot-circle"
case project.ProjectStatus_Disabled:
case checklist.ChecklistStatus_Disabled:
subStatusClass = "text-orange"
subStatusIcon = "far fa-circle"
}
@@ -110,7 +110,7 @@ func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Req
}
loadFunc := func(ctx context.Context, sorting string, fields []datatable.DisplayField) (resp [][]datatable.ColumnValue, err error) {
res, err := h.ProjectRepo.Find(ctx, claims, project.ProjectFindRequest{
res, err := h.ChecklistRepo.Find(ctx, claims, checklist.ChecklistFindRequest{
Where: "account_id = ?",
Args: []interface{}{claims.Audience},
Order: strings.Split(sorting, ","),
@@ -122,7 +122,7 @@ func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Req
for _, a := range res {
l, err := mapFunc(a, fields)
if err != nil {
return resp, errors.Wrapf(err, "Failed to map project for display.")
return resp, errors.Wrapf(err, "Failed to map checklist for display.")
}
resp = append(resp, l)
@@ -148,15 +148,15 @@ func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Req
}
data := map[string]interface{}{
"datatable": dt.Response(),
"urlProjectsCreate": urlProjectsCreate(),
"datatable": dt.Response(),
"urlChecklistsCreate": urlChecklistsCreate(),
}
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "projects-index.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "checklists-index.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
}
// Create handles creating a new project for the account.
func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// Create handles creating a new checklist for the account.
func (h *Checklists) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctxValues, err := webcontext.ContextValues(ctx)
if err != nil {
@@ -169,7 +169,7 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
}
//
req := new(project.ProjectCreateRequest)
req := new(checklist.ChecklistCreateRequest)
data := make(map[string]interface{})
f := func() (bool, error) {
if r.Method == http.MethodPost {
@@ -186,7 +186,7 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
}
req.AccountID = claims.Audience
usr, err := h.ProjectRepo.Create(ctx, claims, *req, ctxValues.Now)
usr, err := h.ChecklistRepo.Create(ctx, claims, *req, ctxValues.Now)
if err != nil {
switch errors.Cause(err) {
default:
@@ -199,12 +199,12 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
}
}
// Display a success message to the project.
// Display a success message to the checklist.
webcontext.SessionFlashSuccess(ctx,
"Project Created",
"Project successfully created.")
"Checklist Created",
"Checklist successfully created.")
return true, web.Redirect(ctx, w, r, urlProjectsView(usr.ID), http.StatusFound)
return true, web.Redirect(ctx, w, r, urlChecklistsView(usr.ID), http.StatusFound)
}
return false, nil
@@ -219,17 +219,17 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
data["form"] = req
if verr, ok := weberror.NewValidationError(ctx, webcontext.Validator().Struct(project.ProjectCreateRequest{})); ok {
if verr, ok := weberror.NewValidationError(ctx, webcontext.Validator().Struct(checklist.ChecklistCreateRequest{})); ok {
data["validationDefaults"] = verr.(*weberror.Error)
}
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "projects-create.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "checklists-create.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
}
// View handles displaying a project.
func (h *Projects) View(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// View handles displaying a checklist.
func (h *Checklists) View(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
projectID := params["project_id"]
checklistID := params["checklist_id"]
ctxValues, err := webcontext.ContextValues(ctx)
if err != nil {
@@ -251,18 +251,18 @@ func (h *Projects) View(ctx context.Context, w http.ResponseWriter, r *http.Requ
switch r.PostForm.Get("action") {
case "archive":
err = h.ProjectRepo.Archive(ctx, claims, project.ProjectArchiveRequest{
ID: projectID,
err = h.ChecklistRepo.Archive(ctx, claims, checklist.ChecklistArchiveRequest{
ID: checklistID,
}, ctxValues.Now)
if err != nil {
return false, err
}
webcontext.SessionFlashSuccess(ctx,
"Project Archive",
"Project successfully archive.")
"Checklist Archive",
"Checklist successfully archive.")
return true, web.Redirect(ctx, w, r, urlProjectsIndex(), http.StatusFound)
return true, web.Redirect(ctx, w, r, urlChecklistsIndex(), http.StatusFound)
}
}
@@ -276,21 +276,21 @@ func (h *Projects) View(ctx context.Context, w http.ResponseWriter, r *http.Requ
return nil
}
prj, err := h.ProjectRepo.ReadByID(ctx, claims, projectID)
prj, err := h.ChecklistRepo.ReadByID(ctx, claims, checklistID)
if err != nil {
return err
}
data["project"] = prj.Response(ctx)
data["urlProjectsView"] = urlProjectsView(projectID)
data["urlProjectsUpdate"] = urlProjectsUpdate(projectID)
data["checklist"] = prj.Response(ctx)
data["urlChecklistsView"] = urlChecklistsView(checklistID)
data["urlChecklistsUpdate"] = urlChecklistsUpdate(checklistID)
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "projects-view.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "checklists-view.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
}
// Update handles updating a project for the account.
func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
// Update handles updating a checklist for the account.
func (h *Checklists) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
projectID := params["project_id"]
checklistID := params["checklist_id"]
ctxValues, err := webcontext.ContextValues(ctx)
if err != nil {
@@ -303,7 +303,7 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
}
//
req := new(project.ProjectUpdateRequest)
req := new(checklist.ChecklistUpdateRequest)
data := make(map[string]interface{})
f := func() (bool, error) {
if r.Method == http.MethodPost {
@@ -318,9 +318,9 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
if err := decoder.Decode(req, r.PostForm); err != nil {
return false, err
}
req.ID = projectID
req.ID = checklistID
err = h.ProjectRepo.Update(ctx, claims, *req, ctxValues.Now)
err = h.ChecklistRepo.Update(ctx, claims, *req, ctxValues.Now)
if err != nil {
switch errors.Cause(err) {
default:
@@ -333,12 +333,12 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
}
}
// Display a success message to the project.
// Display a success message to the checklist.
webcontext.SessionFlashSuccess(ctx,
"Project Updated",
"Project successfully updated.")
"Checklist Updated",
"Checklist successfully updated.")
return true, web.Redirect(ctx, w, r, urlProjectsView(req.ID), http.StatusFound)
return true, web.Redirect(ctx, w, r, urlChecklistsView(req.ID), http.StatusFound)
}
return false, nil
@@ -351,13 +351,13 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
return nil
}
prj, err := h.ProjectRepo.ReadByID(ctx, claims, projectID)
prj, err := h.ChecklistRepo.ReadByID(ctx, claims, checklistID)
if err != nil {
return err
}
data["project"] = prj.Response(ctx)
data["checklist"] = prj.Response(ctx)
data["urlProjectsView"] = urlProjectsView(projectID)
data["urlChecklistsView"] = urlChecklistsView(checklistID)
if req.ID == "" {
req.Name = &prj.Name
@@ -365,9 +365,9 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
}
data["form"] = req
if verr, ok := weberror.NewValidationError(ctx, webcontext.Validator().Struct(project.ProjectUpdateRequest{})); ok {
if verr, ok := weberror.NewValidationError(ctx, webcontext.Validator().Struct(checklist.ChecklistUpdateRequest{})); ok {
data["validationDefaults"] = verr.(*weberror.Error)
}
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "projects-update.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "checklists-update.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
}

View File

@@ -19,9 +19,9 @@ import (
// Root represents the Root API method handler set.
type Root struct {
Renderer web.Renderer
Sitemap *stm.Sitemap
ProjectRoute webroute.ProjectRoute
Renderer web.Renderer
Sitemap *stm.Sitemap
WebRoute webroute.WebRoute
}
// Index determines if the user has authentication and loads the associated page.
@@ -56,7 +56,7 @@ func (h *Root) SitePage(ctx context.Context, w http.ResponseWriter, r *http.Requ
tmpName = "site-api.gohtml"
// http://127.0.0.1:3001/docs/doc.json
swaggerJsonUrl := h.ProjectRoute.ApiDocsJson(true)
swaggerJsonUrl := h.WebRoute.ApiDocsJson(true)
// Load the json file from the API service.
res, err := pester.Get(swaggerJsonUrl)
@@ -92,8 +92,8 @@ func (h *Root) SitePage(ctx context.Context, w http.ResponseWriter, r *http.Requ
return errors.WithStack(err)
}
data["urlApiBaseUri"] = h.ProjectRoute.WebApiUrl(doc.BasePath)
data["urlApiDocs"] = h.ProjectRoute.ApiDocs()
data["urlApiBaseUri"] = h.WebRoute.WebApiUrl(doc.BasePath)
data["urlApiDocs"] = h.WebRoute.ApiDocs()
case "/pricing":
tmpName = "site-pricing.gohtml"
@@ -122,7 +122,7 @@ func (h *Root) RobotTxt(ctx context.Context, w http.ResponseWriter, r *http.Requ
return web.RespondText(ctx, w, txt, http.StatusOK)
}
sitemapUrl := h.ProjectRoute.WebAppUrl("/sitemap.xml")
sitemapUrl := h.WebRoute.WebAppUrl("/sitemap.xml")
txt := fmt.Sprintf("User-agent: *\nDisallow: /ping\nDisallow: /status\nDisallow: /debug/\nSitemap: %s", sitemapUrl)
return web.RespondText(ctx, w, txt, http.StatusOK)

View File

@@ -11,19 +11,19 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/geonames"
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/ikeikeikeike/go-sitemap-generator/v2/stm"
@@ -50,13 +50,13 @@ type AppContext struct {
AuthRepo *user_auth.Repository
SignupRepo *signup.Repository
InviteRepo *invite.Repository
ProjectRepo *project.Repository
ChecklistRepo *checklist.Repository
GeoRepo *geonames.Repository
Authenticator *auth.Authenticator
StaticDir string
TemplateDir string
Renderer web.Renderer
ProjectRoute webroute.ProjectRoute
WebRoute webroute.WebRoute
PreAppMiddleware []web.Middleware
PostAppMiddleware []web.Middleware
AwsSession *session.Session
@@ -105,7 +105,7 @@ func APP(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
// Build a sitemap.
sm := stm.NewSitemap(1)
sm.SetVerbose(false)
sm.SetDefaultHost(appCtx.ProjectRoute.WebAppUrl(""))
sm.SetDefaultHost(appCtx.WebRoute.WebAppUrl(""))
sm.Create()
smLocAddModified := func(loc stm.URL, filename string) {
@@ -121,19 +121,19 @@ func APP(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
sm.Add(loc)
}
// Register project management pages.
p := Projects{
ProjectRepo: appCtx.ProjectRepo,
Redis: appCtx.Redis,
Renderer: appCtx.Renderer,
// Register checklist management pages.
p := Checklists{
ChecklistRepo: appCtx.ChecklistRepo,
Redis: appCtx.Redis,
Renderer: appCtx.Renderer,
}
app.Handle("POST", "/projects/:project_id/update", p.Update, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/projects/:project_id/update", p.Update, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("POST", "/projects/:project_id", p.View, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/projects/:project_id", p.View, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasAuth())
app.Handle("POST", "/projects/create", p.Create, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/projects/create", p.Create, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/projects", p.Index, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasAuth())
app.Handle("POST", "/checklists/:checklist_id/update", p.Update, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/checklists/:checklist_id/update", p.Update, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("POST", "/checklists/:checklist_id", p.View, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/checklists/:checklist_id", p.View, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasAuth())
app.Handle("POST", "/checklists/create", p.Create, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/checklists/create", p.Create, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/checklists", p.Index, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasAuth())
// Register user management pages.
us := Users{
@@ -231,9 +231,9 @@ func APP(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
// Register root
r := Root{
Renderer: appCtx.Renderer,
ProjectRoute: appCtx.ProjectRoute,
Sitemap: sm,
Renderer: appCtx.Renderer,
WebRoute: appCtx.WebRoute,
Sitemap: sm,
}
app.Handle("GET", "/api", r.SitePage)
app.Handle("GET", "/pricing", r.SitePage)

View File

@@ -23,6 +23,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/cmd/web-app/handlers"
"geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
"geeks-accelerator/oss/saas-starter-kit/internal/checklist"
"geeks-accelerator/oss/saas-starter-kit/internal/geonames"
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
@@ -33,13 +34,12 @@ import (
template_renderer "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/tmplrender"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
@@ -440,20 +440,20 @@ func main() {
// =========================================================================
// Init repositories and AppContext
projectRoute, err := webroute.New(cfg.Project.WebApiBaseUrl, cfg.Service.BaseUrl)
webRoute, err := webroute.New(cfg.Project.WebApiBaseUrl, cfg.Service.BaseUrl)
if err != nil {
log.Fatalf("main : project routes : %+v", cfg.Service.BaseUrl, err)
log.Fatalf("main : checklist routes : %+v", cfg.Service.BaseUrl, err)
}
usrRepo := user.NewRepository(masterDb, projectRoute.UserResetPassword, notifyEmail, cfg.Project.SharedSecretKey)
usrRepo := user.NewRepository(masterDb, webRoute.UserResetPassword, notifyEmail, cfg.Project.SharedSecretKey)
usrAccRepo := user_account.NewRepository(masterDb)
accRepo := account.NewRepository(masterDb)
geoRepo := geonames.NewRepository(masterDb)
accPrefRepo := account_preference.NewRepository(masterDb)
authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo)
signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo)
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
prjRepo := project.NewRepository(masterDb)
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, webRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
chklstRepo := checklist.NewRepository(masterDb)
appCtx := &handlers.AppContext{
Log: log,
@@ -463,7 +463,7 @@ func main() {
Redis: redisClient,
TemplateDir: cfg.Service.TemplateDir,
StaticDir: cfg.Service.StaticFiles.Dir,
ProjectRoute: projectRoute,
WebRoute: webRoute,
UserRepo: usrRepo,
UserAccountRepo: usrAccRepo,
AccountRepo: accRepo,
@@ -472,7 +472,7 @@ func main() {
GeoRepo: geoRepo,
SignupRepo: signupRepo,
InviteRepo: inviteRepo,
ProjectRepo: prjRepo,
ChecklistRepo: chklstRepo,
Authenticator: authenticator,
AwsSession: awsSession,
}
@@ -525,13 +525,14 @@ func main() {
staticS3UrlFormatter = func(p string) string {
// When the path starts with a forward slash its referencing a local file,
// make sure the static file prefix is included
if strings.HasPrefix(p, "/") || !strings.HasPrefix(p, "://") {
if (strings.HasPrefix(p, "/") || !strings.HasPrefix(p, "://")) && !strings.HasPrefix(p, cfg.Service.StaticFiles.S3Prefix) {
p = filepath.Join(cfg.Service.StaticFiles.S3Prefix, p)
}
return s3UrlFormatter(p)
}
} else {
staticS3UrlFormatter = projectRoute.WebAppUrl
staticS3UrlFormatter = webRoute.WebAppUrl
}
// staticUrlFormatter is a help function used by template functions defined below.
@@ -920,11 +921,12 @@ func main() {
tmplFuncs["S3ImgUrl"] = func(ctx context.Context, p string, size int) string {
imgUrl := imgUrlFormatter(p)
if cfg.Service.StaticFiles.ImgResizeEnabled {
var rerr error
imgUrl, rerr = img_resize.S3ImgUrl(ctx, redisClient, staticS3UrlFormatter, awsSession, cfg.Aws.S3BucketPublic, imgResizeS3KeyPrefix, imgUrl, size)
if rerr != nil {
imgUrl = "error"
log.Printf("main : S3ImgUrl : %s - %s\n", p, rerr)
log.Printf("main : S3ImgUrl : %s - %+v\n", p, rerr)
}
}
return imgUrl

View File

@@ -1,4 +1,4 @@
{{define "title"}}Create Project{{end}}
{{define "title"}}Create Checklist{{end}}
{{define "style"}}
{{end}}
@@ -6,13 +6,13 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/projects">Projects</a></li>
<li class="breadcrumb-item"><a href="/checklists">Checklists</a></li>
<li class="breadcrumb-item active" aria-current="page">Create</li>
</ol>
</nav>
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Create Project</h1>
<h1 class="h3 mb-0 text-gray-800">Create Checklist</h1>
</div>
<form class="user" method="post" novalidate>
@@ -23,10 +23,10 @@
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="inputName">Project Name</label>
<label for="inputName">Checklist Name</label>
<input type="text" id="inputName"
class="form-control {{ ValidationFieldClass $.validationErrors "Name" }}"
placeholder="Enter name for your project" name="Name"value="{{ .form.Name }}" required>
placeholder="Enter name for your checklist" name="Name"value="{{ .form.Name }}" required>
{{template "invalid-feedback" dict "fieldName" "Name" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
@@ -37,7 +37,7 @@
<div class="row mt-4">
<div class="col">
<input id="btnSubmit" type="submit" name="action" value="Save" class="btn btn-primary"/>
<a href="/projects" class="ml-2 btn btn-secondary" >Cancel</a>
<a href="/checklists" class="ml-2 btn btn-secondary" >Cancel</a>
</div>
</div>

View File

@@ -1,19 +1,19 @@
{{define "title"}}Projects{{end}}
{{define "title"}}Checklists{{end}}
{{define "content"}}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/projects">Projects</a></li>
<li class="breadcrumb-item"><a href="/checklists">Checklists</a></li>
<li class="breadcrumb-item active" aria-current="page">Index</li>
</ol>
</nav>
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Projects</h1>
<h1 class="h3 mb-0 text-gray-800">Checklists</h1>
{{ if HasRole $._Ctx "admin" }}
<a href="{{ .urlProjectsCreate }}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm">
<i class="fas fa-folder-plus fa-sm text-white-50 mr-1"></i>Create Project</a>
<a href="{{ .urlChecklistsCreate }}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm">
<i class="fas fa-folder-plus fa-sm text-white-50 mr-1"></i>Create Checklist</a>
{{ end }}
</div>

View File

@@ -1,4 +1,4 @@
{{define "title"}}Update Project - {{ .project.Name }}{{end}}
{{define "title"}}Update Checklist - {{ .checklist.Name }}{{end}}
{{define "style"}}
{{end}}
@@ -6,14 +6,14 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/projects">Projects</a></li>
<li class="breadcrumb-item"><a href="{{ .urlProjectsView }}">{{ .form.Name }}</a></li>
<li class="breadcrumb-item"><a href="/checklists">Checklists</a></li>
<li class="breadcrumb-item"><a href="{{ .urlChecklistsView }}">{{ .form.Name }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Update</li>
</ol>
</nav>
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Update Project</h1>
<h1 class="h3 mb-0 text-gray-800">Update Checklist</h1>
</div>
<form class="user" method="post" novalidate>
@@ -22,7 +22,7 @@
<div class="row mb-2">
<div class="col-12">
<h4 class="card-title">Project Details</h4>
<h4 class="card-title">Checklist Details</h4>
</div>
</div>
@@ -39,7 +39,7 @@
<label for="selectStatus">Status</label>
<select class="form-control {{ ValidationFieldClass $.validationErrors "Status" }}"
id="selectStatus" name="Status">
{{ range $t := .project.Status.Options }}
{{ range $t := .checklist.Status.Options }}
<option value="{{ $t.Value }}" {{ if $t.Selected }}selected="selected"{{ end }}>{{ $t.Title }}</option>
{{ end }}
</select>

View File

@@ -1,4 +1,4 @@
{{define "title"}}Project - {{ .project.Name }}{{end}}
{{define "title"}}Checklist - {{ .checklist.Name }}{{end}}
{{define "style"}}
{{end}}
@@ -6,29 +6,29 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/projects">Projects</a></li>
<li class="breadcrumb-item"><a href="{{ .urlProjectsView }}">{{ .project.Name }}</a></li>
<li class="breadcrumb-item"><a href="/checklists">Checklists</a></li>
<li class="breadcrumb-item"><a href="{{ .urlChecklistsView }}">{{ .checklist.Name }}</a></li>
<li class="breadcrumb-item active" aria-current="page">View</li>
</ol>
</nav>
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">{{ .project.Name }}</h1>
<!-- a href="{{ .urlProjectsUpdate }}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm"><i class="far fa-edit fa-sm text-white-50 mr-1"></i>Edit Details</a -->
<h1 class="h3 mb-0 text-gray-800">{{ .checklist.Name }}</h1>
<!-- a href="{{ .urlChecklistsUpdate }}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm"><i class="far fa-edit fa-sm text-white-50 mr-1"></i>Edit Details</a -->
</div>
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-dark">Project Details</h6>
<h6 class="m-0 font-weight-bold text-dark">Checklist Details</h6>
<div class="dropdown no-arrow show">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
</a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink" x-placement="bottom-end" style="position: absolute; transform: translate3d(-156px, 19px, 0px); top: 0px; left: 0px; will-change: transform;">
<div class="dropdown-header">Actions</div>
<a class="dropdown-item" href="{{ .urlProjectsUpdate }}">Update Details</a>
<a class="dropdown-item" href="{{ .urlChecklistsUpdate }}">Update Details</a>
{{ if HasRole $._Ctx "admin" }}
<form method="post"><input type="hidden" name="action" value="archive" /><input type="submit" value="Archive Project" class="dropdown-item"></form>
<form method="post"><input type="hidden" name="action" value="archive" /><input type="submit" value="Archive Checklist" class="dropdown-item"></form>
{{ end }}
</div>
</div>
@@ -38,16 +38,16 @@
<div class="col-md-6">
<p>
<small>Name</small><br/>
<b>{{ .project.Name }}</b>
<b>{{ .checklist.Name }}</b>
</p>
<p>
<small>Status</small><br/>
{{ if .project }}
{{ if .checklist }}
<b>
{{ if eq .project.Status.Value "active" }}
<span class="text-green"><i class="fas fa-circle mr-1"></i>{{ .project.Status.Title }}</span>
{{ if eq .checklist.Status.Value "active" }}
<span class="text-green"><i class="fas fa-circle mr-1"></i>{{ .checklist.Status.Title }}</span>
{{else}}
<span class="text-orange"><i class="fas fa-circle-notch mr-1"></i>{{.project.Status.Title }}</span>
<span class="text-orange"><i class="fas fa-circle-notch mr-1"></i>{{.checklist.Status.Title }}</span>
{{end}}
</b>
{{ end }}
@@ -56,7 +56,7 @@
<div class="col-md-6">
<p>
<small>ID</small><br/>
<b>{{ .project.ID }}</b>
<b>{{ .checklist.ID }}</b>
</p>
</div>
</div>

View File

@@ -10,7 +10,7 @@
<p>AWS credentials must be set and then the following configs be set as well.</p>
<pre>
export WEB_APP_SERVICE_S3_BUCKET_PUBLIC=example-bucket-public
export WEB_APP_AWS_S3_BUCKET_PUBLIC=example-bucket-public
export WEB_APP_SERVICE_STATICFILES_IMG_RESIZE_ENABLED=1
</pre>

View File

@@ -7,7 +7,7 @@
<div class="sidebar-brand-icon rotate-n-15">
<i class="fas fa-dragon"></i>
</div>
<div class="sidebar-brand-text mx-3">Example Project</div>
<div class="sidebar-brand-text mx-3">Example Checklist</div>
</a>
{{ if HasAuth $._Ctx }}
@@ -32,13 +32,13 @@
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#navSectionProjects" aria-expanded="true" aria-controls="navSectionProjects">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#navSectionChecklists" aria-expanded="true" aria-controls="navSectionChecklists">
<i class="fas fa-fw fa-layer-group"></i>
<span>Projects</span>
<span>Checklists</span>
</a>
<div id="navSectionProjects" class="collapse" data-parent="#accordionSidebar">
<div id="navSectionChecklists" class="collapse" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<a class="collapse-item" href="/projects">Manage Projects</a>
<a class="collapse-item" href="/checklists">Manage Checklists</a>
</div>
</div>
</li>