1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-17 00:17:59 +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" "application/json"
], ],
"tags": [ "tags": [
"project" "checklist"
], ],
"summary": "List projects", "summary": "List projects",
"parameters": [ "parameters": [
@ -261,7 +261,7 @@ var doc = `{
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/project.ProjectResponse" "$ref": "#/definitions/checklist.ProjectResponse"
} }
} }
}, },
@ -291,7 +291,7 @@ var doc = `{
"OAuth2Password": [] "OAuth2Password": []
} }
], ],
"description": "Create inserts a new project into the system.", "description": "Create inserts a new checklist into the system.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -299,9 +299,9 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"project" "checklist"
], ],
"summary": "Create new project.", "summary": "Create new checklist.",
"parameters": [ "parameters": [
{ {
"description": "Project details", "description": "Project details",
@ -310,7 +310,7 @@ var doc = `{
"required": true, "required": true,
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/project.ProjectCreateRequest" "$ref": "#/definitions/checklist.ProjectCreateRequest"
} }
} }
], ],
@ -318,7 +318,7 @@ var doc = `{
"201": { "201": {
"description": "Created", "description": "Created",
"schema": { "schema": {
"$ref": "#/definitions/project.ProjectResponse" "$ref": "#/definitions/checklist.ProjectResponse"
} }
}, },
"400": { "400": {
@ -353,7 +353,7 @@ var doc = `{
"OAuth2Password": [] "OAuth2Password": []
} }
], ],
"description": "Update updates the specified project in the system.", "description": "Update updates the specified checklist in the system.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -361,9 +361,9 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"project" "checklist"
], ],
"summary": "Update project by ID", "summary": "Update checklist by ID",
"parameters": [ "parameters": [
{ {
"description": "Update fields", "description": "Update fields",
@ -372,7 +372,7 @@ var doc = `{
"required": true, "required": true,
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/project.ProjectUpdateRequest" "$ref": "#/definitions/checklist.ProjectUpdateRequest"
} }
} }
], ],
@ -406,7 +406,7 @@ var doc = `{
"OAuth2Password": [] "OAuth2Password": []
} }
], ],
"description": "Archive soft-deletes the specified project from the system.", "description": "Archive soft-deletes the specified checklist from the system.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -414,9 +414,9 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"project" "checklist"
], ],
"summary": "Archive project by ID", "summary": "Archive checklist by ID",
"parameters": [ "parameters": [
{ {
"description": "Update fields", "description": "Update fields",
@ -425,7 +425,7 @@ var doc = `{
"required": true, "required": true,
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/project.ProjectArchiveRequest" "$ref": "#/definitions/checklist.ProjectArchiveRequest"
} }
} }
], ],
@ -459,7 +459,7 @@ var doc = `{
"OAuth2Password": [] "OAuth2Password": []
} }
], ],
"description": "Read returns the specified project from the system.", "description": "Read returns the specified checklist from the system.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -467,9 +467,9 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"project" "checklist"
], ],
"summary": "Get project by ID.", "summary": "Get checklist by ID.",
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
@ -483,7 +483,7 @@ var doc = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/project.ProjectResponse" "$ref": "#/definitions/checklist.ProjectResponse"
} }
}, },
"400": { "400": {
@ -512,7 +512,7 @@ var doc = `{
"OAuth2Password": [] "OAuth2Password": []
} }
], ],
"description": "Delete removes the specified project from the system.", "description": "Delete removes the specified checklist from the system.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -520,9 +520,9 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"project" "checklist"
], ],
"summary": "Delete project by ID", "summary": "Delete checklist by ID",
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
@ -1512,7 +1512,7 @@ var doc = `{
} }
} }
}, },
"project.ProjectArchiveRequest": { "checklist.ProjectArchiveRequest": {
"type": "object", "type": "object",
"required": [ "required": [
"id" "id"
@ -1524,7 +1524,7 @@ var doc = `{
} }
} }
}, },
"project.ProjectCreateRequest": { "checklist.ProjectCreateRequest": {
"type": "object", "type": "object",
"required": [ "required": [
"account_id", "account_id",
@ -1541,7 +1541,7 @@ var doc = `{
} }
} }
}, },
"project.ProjectResponse": { "checklist.ProjectResponse": {
"type": "object", "type": "object",
"required": [ "required": [
"account_id", "account_id",
@ -1579,7 +1579,7 @@ var doc = `{
} }
} }
}, },
"project.ProjectUpdateRequest": { "checklist.ProjectUpdateRequest": {
"type": "object", "type": "object",
"required": [ "required": [
"id" "id"

View File

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

View File

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

View File

@ -4,18 +4,18 @@ import (
"context" "context"
"net/http" "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/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "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/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Example represents the Example API method handler set. // Example represents the Example API method handler set.
type Example struct { type Example struct {
Project *project.Repository Checklist *checklist.Repository
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE. // 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 != "" { 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) 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"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference" "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/mid"
saasSwagger "geeks-accelerator/oss/saas-starter-kit/internal/mid/saas-swagger" 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/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "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/webcontext"
_ "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" _ "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/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "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"
@ -36,7 +36,7 @@ type AppContext struct {
AuthRepo *user_auth.Repository AuthRepo *user_auth.Repository
SignupRepo *signup.Repository SignupRepo *signup.Repository
InviteRepo *invite.Repository InviteRepo *invite.Repository
ProjectRepo *project.Repository ChecklistRepo *checklist.Repository
Authenticator *auth.Authenticator Authenticator *auth.Authenticator
PreAppMiddleware []web.Middleware PreAppMiddleware []web.Middleware
PostAppMiddleware []web.Middleware PostAppMiddleware []web.Middleware
@ -74,7 +74,7 @@ func API(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
// Register example endpoints. // Register example endpoints.
ex := Example{ ex := Example{
Project: appCtx.ProjectRepo, Checklist: appCtx.ChecklistRepo,
} }
app.Handle("GET", "/v1/examples/error-response", ex.ErrorResponse) 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) app.Handle("POST", "/v1/signup", s.Signup)
// Register project. // Register checklist.
p := Projects{ p := Checklists{
Repository: appCtx.ProjectRepo, Repository: appCtx.ChecklistRepo,
} }
app.Handle("GET", "/v1/projects", p.Find, mid.AuthenticateHeader(appCtx.Authenticator)) app.Handle("GET", "/v1/checklists", p.Find, mid.AuthenticateHeader(appCtx.Authenticator))
app.Handle("POST", "/v1/projects", p.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin)) app.Handle("POST", "/v1/checklists", p.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle("GET", "/v1/projects/:id", p.Read, mid.AuthenticateHeader(appCtx.Authenticator)) app.Handle("GET", "/v1/checklists/: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/checklists", 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("PATCH", "/v1/checklists/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("DELETE", "/v1/checklists/:id", p.Delete, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
// Register swagger documentation. // Register swagger documentation.
// TODO: Add authentication. Current authenticator requires an Authorization header // 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/cmd/web-api/handlers"
"geeks-accelerator/oss/saas-starter-kit/internal/account" "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/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/mid"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "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/flag"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/notify" "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/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/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "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"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite" "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/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
@ -437,19 +437,19 @@ func main() {
// ========================================================================= // =========================================================================
// Init repositories and AppContext // 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 { 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) usrAccRepo := user_account.NewRepository(masterDb)
accRepo := account.NewRepository(masterDb) accRepo := account.NewRepository(masterDb)
accPrefRepo := account_preference.NewRepository(masterDb) accPrefRepo := account_preference.NewRepository(masterDb)
authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo) authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo)
signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo) signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo)
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey) inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, webRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
prjRepo := project.NewRepository(masterDb) chklstRepo := checklist.NewRepository(masterDb)
appCtx := &handlers.AppContext{ appCtx := &handlers.AppContext{
Log: log, Log: log,
@ -463,7 +463,7 @@ func main() {
AuthRepo: authRepo, AuthRepo: authRepo,
SignupRepo: signupRepo, SignupRepo: signupRepo,
InviteRepo: inviteRepo, InviteRepo: inviteRepo,
ProjectRepo: prjRepo, ChecklistRepo: chklstRepo,
Authenticator: authenticator, Authenticator: authenticator,
} }

View File

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

View File

@ -6,50 +6,50 @@ import (
"net/http" "net/http"
"strings" "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/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/datatable" "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"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "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/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/gorilla/schema" "github.com/gorilla/schema"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis" "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
) )
// Projects represents the Projects API method handler set. // Checklists represents the Checklists API method handler set.
type Projects struct { type Checklists struct {
ProjectRepo *project.Repository ChecklistRepo *checklist.Repository
Redis *redis.Client Redis *redis.Client
Renderer web.Renderer Renderer web.Renderer
} }
func urlProjectsIndex() string { func urlChecklistsIndex() string {
return fmt.Sprintf("/projects") return fmt.Sprintf("/checklists")
} }
func urlProjectsCreate() string { func urlChecklistsCreate() string {
return fmt.Sprintf("/projects/create") return fmt.Sprintf("/checklists/create")
} }
func urlProjectsView(projectID string) string { func urlChecklistsView(checklistID string) string {
return fmt.Sprintf("/projects/%s", projectID) return fmt.Sprintf("/checklists/%s", checklistID)
} }
func urlProjectsUpdate(projectID string) string { func urlChecklistsUpdate(checklistID string) string {
return fmt.Sprintf("/projects/%s/update", projectID) return fmt.Sprintf("/checklists/%s/update", checklistID)
} }
// Index handles listing all the projects for the current account. // Index handles listing all the checklists for the current account.
func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (h *Checklists) Index(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
claims, err := auth.ClaimsFromContext(ctx) claims, err := auth.ClaimsFromContext(ctx)
if err != nil { if err != nil {
return err return err
} }
statusOpts := web.NewEnumResponse(ctx, nil, project.ProjectStatus_ValuesInterface()...) statusOpts := web.NewEnumResponse(ctx, nil, checklist.ChecklistStatus_ValuesInterface()...)
statusFilterItems := []datatable.FilterOptionItem{} statusFilterItems := []datatable.FilterOptionItem{}
for _, opt := range statusOpts.Options { 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{ fields := []datatable.DisplayField{
datatable.DisplayField{Field: "id", Title: "ID", Visible: false, Searchable: true, Orderable: true, Filterable: false}, 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: "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: "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}, 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++ { for i := 0; i < len(cols); i++ {
col := cols[i] col := cols[i]
var v datatable.ColumnValue 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) v.Value = fmt.Sprintf("%s", q.ID)
case "name": case "name":
v.Value = q.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": case "status":
v.Value = q.Status.String() v.Value = q.Status.String()
var subStatusClass string var subStatusClass string
var subStatusIcon string var subStatusIcon string
switch q.Status { switch q.Status {
case project.ProjectStatus_Active: case checklist.ChecklistStatus_Active:
subStatusClass = "text-green" subStatusClass = "text-green"
subStatusIcon = "far fa-dot-circle" subStatusIcon = "far fa-dot-circle"
case project.ProjectStatus_Disabled: case checklist.ChecklistStatus_Disabled:
subStatusClass = "text-orange" subStatusClass = "text-orange"
subStatusIcon = "far fa-circle" 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) { 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 = ?", Where: "account_id = ?",
Args: []interface{}{claims.Audience}, Args: []interface{}{claims.Audience},
Order: strings.Split(sorting, ","), 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 { for _, a := range res {
l, err := mapFunc(a, fields) l, err := mapFunc(a, fields)
if err != nil { 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) resp = append(resp, l)
@ -149,14 +149,14 @@ func (h *Projects) Index(ctx context.Context, w http.ResponseWriter, r *http.Req
data := map[string]interface{}{ data := map[string]interface{}{
"datatable": dt.Response(), "datatable": dt.Response(),
"urlProjectsCreate": urlProjectsCreate(), "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. // Create handles creating a new checklist for the account.
func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (h *Checklists) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctxValues, err := webcontext.ContextValues(ctx) ctxValues, err := webcontext.ContextValues(ctx)
if err != nil { 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{}) data := make(map[string]interface{})
f := func() (bool, error) { f := func() (bool, error) {
if r.Method == http.MethodPost { 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 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 { if err != nil {
switch errors.Cause(err) { switch errors.Cause(err) {
default: 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, webcontext.SessionFlashSuccess(ctx,
"Project Created", "Checklist Created",
"Project successfully 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 return false, nil
@ -219,17 +219,17 @@ func (h *Projects) Create(ctx context.Context, w http.ResponseWriter, r *http.Re
data["form"] = req 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) 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. // View handles displaying a checklist.
func (h *Projects) View(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { 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) ctxValues, err := webcontext.ContextValues(ctx)
if err != nil { 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") { switch r.PostForm.Get("action") {
case "archive": case "archive":
err = h.ProjectRepo.Archive(ctx, claims, project.ProjectArchiveRequest{ err = h.ChecklistRepo.Archive(ctx, claims, checklist.ChecklistArchiveRequest{
ID: projectID, ID: checklistID,
}, ctxValues.Now) }, ctxValues.Now)
if err != nil { if err != nil {
return false, err return false, err
} }
webcontext.SessionFlashSuccess(ctx, webcontext.SessionFlashSuccess(ctx,
"Project Archive", "Checklist Archive",
"Project successfully 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 return nil
} }
prj, err := h.ProjectRepo.ReadByID(ctx, claims, projectID) prj, err := h.ChecklistRepo.ReadByID(ctx, claims, checklistID)
if err != nil { if err != nil {
return err return err
} }
data["project"] = prj.Response(ctx) data["checklist"] = prj.Response(ctx)
data["urlProjectsView"] = urlProjectsView(projectID) data["urlChecklistsView"] = urlChecklistsView(checklistID)
data["urlProjectsUpdate"] = urlProjectsUpdate(projectID) 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. // Update handles updating a checklist for the account.
func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { 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) ctxValues, err := webcontext.ContextValues(ctx)
if err != nil { 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{}) data := make(map[string]interface{})
f := func() (bool, error) { f := func() (bool, error) {
if r.Method == http.MethodPost { 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 { if err := decoder.Decode(req, r.PostForm); err != nil {
return false, err 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 { if err != nil {
switch errors.Cause(err) { switch errors.Cause(err) {
default: 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, webcontext.SessionFlashSuccess(ctx,
"Project Updated", "Checklist Updated",
"Project successfully 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 return false, nil
@ -351,13 +351,13 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
return nil return nil
} }
prj, err := h.ProjectRepo.ReadByID(ctx, claims, projectID) prj, err := h.ChecklistRepo.ReadByID(ctx, claims, checklistID)
if err != nil { if err != nil {
return err return err
} }
data["project"] = prj.Response(ctx) data["checklist"] = prj.Response(ctx)
data["urlProjectsView"] = urlProjectsView(projectID) data["urlChecklistsView"] = urlChecklistsView(checklistID)
if req.ID == "" { if req.ID == "" {
req.Name = &prj.Name req.Name = &prj.Name
@ -365,9 +365,9 @@ func (h *Projects) Update(ctx context.Context, w http.ResponseWriter, r *http.Re
} }
data["form"] = req 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) 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

@ -21,7 +21,7 @@ import (
type Root struct { type Root struct {
Renderer web.Renderer Renderer web.Renderer
Sitemap *stm.Sitemap Sitemap *stm.Sitemap
ProjectRoute webroute.ProjectRoute WebRoute webroute.WebRoute
} }
// Index determines if the user has authentication and loads the associated page. // 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" tmpName = "site-api.gohtml"
// http://127.0.0.1:3001/docs/doc.json // 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. // Load the json file from the API service.
res, err := pester.Get(swaggerJsonUrl) 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) return errors.WithStack(err)
} }
data["urlApiBaseUri"] = h.ProjectRoute.WebApiUrl(doc.BasePath) data["urlApiBaseUri"] = h.WebRoute.WebApiUrl(doc.BasePath)
data["urlApiDocs"] = h.ProjectRoute.ApiDocs() data["urlApiDocs"] = h.WebRoute.ApiDocs()
case "/pricing": case "/pricing":
tmpName = "site-pricing.gohtml" 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) 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) txt := fmt.Sprintf("User-agent: *\nDisallow: /ping\nDisallow: /status\nDisallow: /debug/\nSitemap: %s", sitemapUrl)
return web.RespondText(ctx, w, txt, http.StatusOK) 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"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference" "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/geonames"
"geeks-accelerator/oss/saas-starter-kit/internal/mid" "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/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "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/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "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/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "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"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite" "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/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/ikeikeikeike/go-sitemap-generator/v2/stm" "github.com/ikeikeikeike/go-sitemap-generator/v2/stm"
@ -50,13 +50,13 @@ type AppContext struct {
AuthRepo *user_auth.Repository AuthRepo *user_auth.Repository
SignupRepo *signup.Repository SignupRepo *signup.Repository
InviteRepo *invite.Repository InviteRepo *invite.Repository
ProjectRepo *project.Repository ChecklistRepo *checklist.Repository
GeoRepo *geonames.Repository GeoRepo *geonames.Repository
Authenticator *auth.Authenticator Authenticator *auth.Authenticator
StaticDir string StaticDir string
TemplateDir string TemplateDir string
Renderer web.Renderer Renderer web.Renderer
ProjectRoute webroute.ProjectRoute WebRoute webroute.WebRoute
PreAppMiddleware []web.Middleware PreAppMiddleware []web.Middleware
PostAppMiddleware []web.Middleware PostAppMiddleware []web.Middleware
AwsSession *session.Session AwsSession *session.Session
@ -105,7 +105,7 @@ func APP(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
// Build a sitemap. // Build a sitemap.
sm := stm.NewSitemap(1) sm := stm.NewSitemap(1)
sm.SetVerbose(false) sm.SetVerbose(false)
sm.SetDefaultHost(appCtx.ProjectRoute.WebAppUrl("")) sm.SetDefaultHost(appCtx.WebRoute.WebAppUrl(""))
sm.Create() sm.Create()
smLocAddModified := func(loc stm.URL, filename string) { smLocAddModified := func(loc stm.URL, filename string) {
@ -121,19 +121,19 @@ func APP(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
sm.Add(loc) sm.Add(loc)
} }
// Register project management pages. // Register checklist management pages.
p := Projects{ p := Checklists{
ProjectRepo: appCtx.ProjectRepo, ChecklistRepo: appCtx.ChecklistRepo,
Redis: appCtx.Redis, Redis: appCtx.Redis,
Renderer: appCtx.Renderer, Renderer: appCtx.Renderer,
} }
app.Handle("POST", "/projects/:project_id/update", p.Update, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin)) app.Handle("POST", "/checklists/:checklist_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("GET", "/checklists/:checklist_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("POST", "/checklists/:checklist_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("GET", "/checklists/:checklist_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("POST", "/checklists/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", "/checklists/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("GET", "/checklists", p.Index, mid.AuthenticateSessionRequired(appCtx.Authenticator), mid.HasAuth())
// Register user management pages. // Register user management pages.
us := Users{ us := Users{
@ -232,7 +232,7 @@ func APP(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
// Register root // Register root
r := Root{ r := Root{
Renderer: appCtx.Renderer, Renderer: appCtx.Renderer,
ProjectRoute: appCtx.ProjectRoute, WebRoute: appCtx.WebRoute,
Sitemap: sm, Sitemap: sm,
} }
app.Handle("GET", "/api", r.SitePage) app.Handle("GET", "/api", 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/cmd/web-app/handlers"
"geeks-accelerator/oss/saas-starter-kit/internal/account" "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/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/geonames"
"geeks-accelerator/oss/saas-starter-kit/internal/mid" "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/auth"
@ -33,13 +34,12 @@ import (
template_renderer "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/tmplrender" 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/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "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/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "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"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite" "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/user_auth"
"geeks-accelerator/oss/saas-starter-kit/internal/webroute"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
@ -440,20 +440,20 @@ func main() {
// ========================================================================= // =========================================================================
// Init repositories and AppContext // 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 { 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) usrAccRepo := user_account.NewRepository(masterDb)
accRepo := account.NewRepository(masterDb) accRepo := account.NewRepository(masterDb)
geoRepo := geonames.NewRepository(masterDb) geoRepo := geonames.NewRepository(masterDb)
accPrefRepo := account_preference.NewRepository(masterDb) accPrefRepo := account_preference.NewRepository(masterDb)
authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo) authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo)
signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo) signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo)
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey) inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, webRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
prjRepo := project.NewRepository(masterDb) chklstRepo := checklist.NewRepository(masterDb)
appCtx := &handlers.AppContext{ appCtx := &handlers.AppContext{
Log: log, Log: log,
@ -463,7 +463,7 @@ func main() {
Redis: redisClient, Redis: redisClient,
TemplateDir: cfg.Service.TemplateDir, TemplateDir: cfg.Service.TemplateDir,
StaticDir: cfg.Service.StaticFiles.Dir, StaticDir: cfg.Service.StaticFiles.Dir,
ProjectRoute: projectRoute, WebRoute: webRoute,
UserRepo: usrRepo, UserRepo: usrRepo,
UserAccountRepo: usrAccRepo, UserAccountRepo: usrAccRepo,
AccountRepo: accRepo, AccountRepo: accRepo,
@ -472,7 +472,7 @@ func main() {
GeoRepo: geoRepo, GeoRepo: geoRepo,
SignupRepo: signupRepo, SignupRepo: signupRepo,
InviteRepo: inviteRepo, InviteRepo: inviteRepo,
ProjectRepo: prjRepo, ChecklistRepo: chklstRepo,
Authenticator: authenticator, Authenticator: authenticator,
AwsSession: awsSession, AwsSession: awsSession,
} }
@ -525,13 +525,14 @@ func main() {
staticS3UrlFormatter = func(p string) string { staticS3UrlFormatter = func(p string) string {
// When the path starts with a forward slash its referencing a local file, // When the path starts with a forward slash its referencing a local file,
// make sure the static file prefix is included // 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) p = filepath.Join(cfg.Service.StaticFiles.S3Prefix, p)
} }
return s3UrlFormatter(p) return s3UrlFormatter(p)
} }
} else { } else {
staticS3UrlFormatter = projectRoute.WebAppUrl staticS3UrlFormatter = webRoute.WebAppUrl
} }
// staticUrlFormatter is a help function used by template functions defined below. // 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 { tmplFuncs["S3ImgUrl"] = func(ctx context.Context, p string, size int) string {
imgUrl := imgUrlFormatter(p) imgUrl := imgUrlFormatter(p)
if cfg.Service.StaticFiles.ImgResizeEnabled { if cfg.Service.StaticFiles.ImgResizeEnabled {
var rerr error var rerr error
imgUrl, rerr = img_resize.S3ImgUrl(ctx, redisClient, staticS3UrlFormatter, awsSession, cfg.Aws.S3BucketPublic, imgResizeS3KeyPrefix, imgUrl, size) imgUrl, rerr = img_resize.S3ImgUrl(ctx, redisClient, staticS3UrlFormatter, awsSession, cfg.Aws.S3BucketPublic, imgResizeS3KeyPrefix, imgUrl, size)
if rerr != nil { if rerr != nil {
imgUrl = "error" imgUrl = "error"
log.Printf("main : S3ImgUrl : %s - %s\n", p, rerr) log.Printf("main : S3ImgUrl : %s - %+v\n", p, rerr)
} }
} }
return imgUrl return imgUrl

View File

@ -1,4 +1,4 @@
{{define "title"}}Create Project{{end}} {{define "title"}}Create Checklist{{end}}
{{define "style"}} {{define "style"}}
{{end}} {{end}}
@ -6,13 +6,13 @@
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="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> <li class="breadcrumb-item active" aria-current="page">Create</li>
</ol> </ol>
</nav> </nav>
<div class="d-sm-flex align-items-center justify-content-between mb-4"> <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> </div>
<form class="user" method="post" novalidate> <form class="user" method="post" novalidate>
@ -23,10 +23,10 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label for="inputName">Project Name</label> <label for="inputName">Checklist Name</label>
<input type="text" id="inputName" <input type="text" id="inputName"
class="form-control {{ ValidationFieldClass $.validationErrors "Name" }}" 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 }} {{template "invalid-feedback" dict "fieldName" "Name" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div> </div>
</div> </div>
@ -37,7 +37,7 @@
<div class="row mt-4"> <div class="row mt-4">
<div class="col"> <div class="col">
<input id="btnSubmit" type="submit" name="action" value="Save" class="btn btn-primary"/> <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>
</div> </div>

View File

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

View File

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

View File

@ -1,4 +1,4 @@
{{define "title"}}Project - {{ .project.Name }}{{end}} {{define "title"}}Checklist - {{ .checklist.Name }}{{end}}
{{define "style"}} {{define "style"}}
{{end}} {{end}}
@ -6,29 +6,29 @@
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="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"><a href="{{ .urlProjectsView }}">{{ .project.Name }}</a></li> <li class="breadcrumb-item"><a href="{{ .urlChecklistsView }}">{{ .checklist.Name }}</a></li>
<li class="breadcrumb-item active" aria-current="page">View</li> <li class="breadcrumb-item active" aria-current="page">View</li>
</ol> </ol>
</nav> </nav>
<div class="d-sm-flex align-items-center justify-content-between mb-4"> <div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">{{ .project.Name }}</h1> <h1 class="h3 mb-0 text-gray-800">{{ .checklist.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 --> <!-- 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>
<div class="card shadow mb-4"> <div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <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"> <div class="dropdown no-arrow show">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <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> <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
</a> </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-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> <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" }} {{ 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 }} {{ end }}
</div> </div>
</div> </div>
@ -38,16 +38,16 @@
<div class="col-md-6"> <div class="col-md-6">
<p> <p>
<small>Name</small><br/> <small>Name</small><br/>
<b>{{ .project.Name }}</b> <b>{{ .checklist.Name }}</b>
</p> </p>
<p> <p>
<small>Status</small><br/> <small>Status</small><br/>
{{ if .project }} {{ if .checklist }}
<b> <b>
{{ if eq .project.Status.Value "active" }} {{ if eq .checklist.Status.Value "active" }}
<span class="text-green"><i class="fas fa-circle mr-1"></i>{{ .project.Status.Title }}</span> <span class="text-green"><i class="fas fa-circle mr-1"></i>{{ .checklist.Status.Title }}</span>
{{else}} {{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}} {{end}}
</b> </b>
{{ end }} {{ end }}
@ -56,7 +56,7 @@
<div class="col-md-6"> <div class="col-md-6">
<p> <p>
<small>ID</small><br/> <small>ID</small><br/>
<b>{{ .project.ID }}</b> <b>{{ .checklist.ID }}</b>
</p> </p>
</div> </div>
</div> </div>

View File

@ -10,7 +10,7 @@
<p>AWS credentials must be set and then the following configs be set as well.</p> <p>AWS credentials must be set and then the following configs be set as well.</p>
<pre> <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 export WEB_APP_SERVICE_STATICFILES_IMG_RESIZE_ENABLED=1
</pre> </pre>

View File

@ -7,7 +7,7 @@
<div class="sidebar-brand-icon rotate-n-15"> <div class="sidebar-brand-icon rotate-n-15">
<i class="fas fa-dragon"></i> <i class="fas fa-dragon"></i>
</div> </div>
<div class="sidebar-brand-text mx-3">Example Project</div> <div class="sidebar-brand-text mx-3">Example Checklist</div>
</a> </a>
{{ if HasAuth $._Ctx }} {{ if HasAuth $._Ctx }}
@ -32,13 +32,13 @@
<!-- Nav Item - Pages Collapse Menu --> <!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item"> <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> <i class="fas fa-fw fa-layer-group"></i>
<span>Projects</span> <span>Checklists</span>
</a> </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"> <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>
</div> </div>
</li> </li>

View File

@ -36,7 +36,7 @@ services:
entrypoint: redis-server --appendonly yes entrypoint: redis-server --appendonly yes
datadog: datadog:
image: example-project/datadog:latest image: example-checklist/datadog:latest
build: build:
context: build/docker/datadog-agent context: build/docker/datadog-agent
dockerfile: Dockerfile dockerfile: Dockerfile
@ -60,7 +60,7 @@ services:
- DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true - DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true
- DD_EXPVAR=service_name=web-app env=dev url=http://web-app:4000/debug/vars|service_name=web-api env=dev url=http://web-api:4001/debug/vars - DD_EXPVAR=service_name=web-app env=dev url=http://web-app:4000/debug/vars|service_name=web-api env=dev url=http://web-api:4001/debug/vars
web-app: web-app:
image: example-project/web-app:latest image: example-checklist/web-app:latest
build: build:
context: . context: .
target: dev target: dev
@ -102,7 +102,7 @@ services:
# - GODEBUG=gctrace=1 # - GODEBUG=gctrace=1
web-api: web-api:
image: example-project/web-api:latest image: example-checklist/web-api:latest
build: build:
context: . context: .
target: dev target: dev

View File

@ -1,4 +1,4 @@
package project package checklist
import ( import (
"context" "context"
@ -15,8 +15,8 @@ import (
) )
const ( const (
// The database table for Project // The database table for Checklist
projectTableName = "projects" checklistTableName = "checklists"
) )
var ( var (
@ -27,14 +27,14 @@ var (
ErrForbidden = errors.New("Attempted action is not allowed") ErrForbidden = errors.New("Attempted action is not allowed")
) )
// CanReadProject determines if claims has the authority to access the specified project by id. // CanReadChecklist determines if claims has the authority to access the specified checklist by id.
func (repo *Repository) CanReadProject(ctx context.Context, claims auth.Claims, id string) error { func (repo *Repository) CanReadChecklist(ctx context.Context, claims auth.Claims, id string) error {
// If the request has claims from a specific project, ensure that the claims // If the request has claims from a specific checklist, ensure that the claims
// has the correct access to the project. // has the correct access to the checklist.
if claims.Audience != "" { if claims.Audience != "" {
// select id from projects where account_id = [accountID] // select id from checklists where account_id = [accountID]
query := sqlbuilder.NewSelectBuilder().Select("id").From(projectTableName) query := sqlbuilder.NewSelectBuilder().Select("id").From(checklistTableName)
query.Where(query.And( query.Where(query.And(
query.Equal("account_id", claims.Audience), query.Equal("account_id", claims.Audience),
query.Equal("ID", id), query.Equal("ID", id),
@ -50,7 +50,7 @@ func (repo *Repository) CanReadProject(ctx context.Context, claims auth.Claims,
} }
// When there is no id returned, then the current claim user does not have access // When there is no id returned, then the current claim user does not have access
// to the specified project. // to the specified checklist.
if id == "" { if id == "" {
return errors.WithStack(ErrForbidden) return errors.WithStack(ErrForbidden)
} }
@ -60,14 +60,14 @@ func (repo *Repository) CanReadProject(ctx context.Context, claims auth.Claims,
return nil return nil
} }
// CanModifyProject determines if claims has the authority to modify the specified project by id. // CanModifyChecklist determines if claims has the authority to modify the specified checklist by id.
func (repo *Repository) CanModifyProject(ctx context.Context, claims auth.Claims, id string) error { func (repo *Repository) CanModifyChecklist(ctx context.Context, claims auth.Claims, id string) error {
err := repo.CanReadProject(ctx, claims, id) err := repo.CanReadChecklist(ctx, claims, id)
if err != nil { if err != nil {
return err return err
} }
// Admin users can update projects they have access to. // Admin users can update checklists they have access to.
if !claims.HasRole(auth.RoleAdmin) { if !claims.HasRole(auth.RoleAdmin) {
return errors.WithStack(ErrForbidden) return errors.WithStack(ErrForbidden)
} }
@ -88,21 +88,21 @@ func applyClaimsSelect(ctx context.Context, claims auth.Claims, query *sqlbuilde
return nil return nil
} }
// projectMapColumns is the list of columns needed for find. // checklistMapColumns is the list of columns needed for find.
var projectMapColumns = "id,account_id,name,status,created_at,updated_at,archived_at" var checklistMapColumns = "id,account_id,name,status,created_at,updated_at,archived_at"
// selectQuery constructs a base select query for Project. // selectQuery constructs a base select query for Checklist.
func selectQuery() *sqlbuilder.SelectBuilder { func selectQuery() *sqlbuilder.SelectBuilder {
query := sqlbuilder.NewSelectBuilder() query := sqlbuilder.NewSelectBuilder()
query.Select(projectMapColumns) query.Select(checklistMapColumns)
query.From(projectTableName) query.From(checklistTableName)
return query return query
} }
// findRequestQuery generates the select query for the given find request. // findRequestQuery generates the select query for the given find request.
// TODO: Need to figure out why can't parse the args when appending the where // TODO: Need to figure out why can't parse the args when appending the where
// to the query. // to the query.
func findRequestQuery(req ProjectFindRequest) (*sqlbuilder.SelectBuilder, []interface{}) { func findRequestQuery(req ChecklistFindRequest) (*sqlbuilder.SelectBuilder, []interface{}) {
query := selectQuery() query := selectQuery()
if req.Where != "" { if req.Where != "" {
@ -124,19 +124,19 @@ func findRequestQuery(req ProjectFindRequest) (*sqlbuilder.SelectBuilder, []inte
return query, req.Args return query, req.Args
} }
// Find gets all the projects from the database based on the request params. // Find gets all the checklists from the database based on the request params.
func (repo *Repository) Find(ctx context.Context, claims auth.Claims, req ProjectFindRequest) (Projects, error) { func (repo *Repository) Find(ctx context.Context, claims auth.Claims, req ChecklistFindRequest) (Checklists, error) {
query, args := findRequestQuery(req) query, args := findRequestQuery(req)
return find(ctx, claims, repo.DbConn, query, args, req.IncludeArchived) return find(ctx, claims, repo.DbConn, query, args, req.IncludeArchived)
} }
// find internal method for getting all the projects from the database using a select query. // find internal method for getting all the checklists from the database using a select query.
func find(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, query *sqlbuilder.SelectBuilder, args []interface{}, includedArchived bool) (Projects, error) { func find(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, query *sqlbuilder.SelectBuilder, args []interface{}, includedArchived bool) (Checklists, error) {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Find") span, ctx := tracer.StartSpanFromContext(ctx, "internal.checklist.Find")
defer span.Finish() defer span.Finish()
query.Select(projectMapColumns) query.Select(checklistMapColumns)
query.From(projectTableName) query.From(checklistTableName)
if !includedArchived { if !includedArchived {
query.Where(query.IsNull("archived_at")) query.Where(query.IsNull("archived_at"))
} }
@ -154,15 +154,15 @@ func find(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, query *sqlbu
rows, err := dbConn.QueryContext(ctx, queryStr, args...) rows, err := dbConn.QueryContext(ctx, queryStr, args...)
if err != nil { if err != nil {
err = errors.Wrapf(err, "query - %s", query.String()) err = errors.Wrapf(err, "query - %s", query.String())
err = errors.WithMessage(err, "find projects failed") err = errors.WithMessage(err, "find checklists failed")
return nil, err return nil, err
} }
// Iterate over each row. // Iterate over each row.
resp := []*Project{} resp := []*Checklist{}
for rows.Next() { for rows.Next() {
var ( var (
m Project m Checklist
err error err error
) )
err = rows.Scan(&m.ID, &m.AccountID, &m.Name, &m.Status, &m.CreatedAt, &m.UpdatedAt, &m.ArchivedAt) err = rows.Scan(&m.ID, &m.AccountID, &m.Name, &m.Status, &m.CreatedAt, &m.UpdatedAt, &m.ArchivedAt)
@ -177,17 +177,17 @@ func find(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, query *sqlbu
return resp, nil return resp, nil
} }
// ReadByID gets the specified project by ID from the database. // ReadByID gets the specified checklist by ID from the database.
func (repo *Repository) ReadByID(ctx context.Context, claims auth.Claims, id string) (*Project, error) { func (repo *Repository) ReadByID(ctx context.Context, claims auth.Claims, id string) (*Checklist, error) {
return repo.Read(ctx, claims, ProjectReadRequest{ return repo.Read(ctx, claims, ChecklistReadRequest{
ID: id, ID: id,
IncludeArchived: false, IncludeArchived: false,
}) })
} }
// Read gets the specified project from the database. // Read gets the specified checklist from the database.
func (repo *Repository) Read(ctx context.Context, claims auth.Claims, req ProjectReadRequest) (*Project, error) { func (repo *Repository) Read(ctx context.Context, claims auth.Claims, req ChecklistReadRequest) (*Checklist, error) {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Read") span, ctx := tracer.StartSpanFromContext(ctx, "internal.checklist.Read")
defer span.Finish() defer span.Finish()
// Validate the request. // Validate the request.
@ -205,7 +205,7 @@ func (repo *Repository) Read(ctx context.Context, claims auth.Claims, req Projec
if err != nil { if err != nil {
return nil, err return nil, err
} else if res == nil || len(res) == 0 { } else if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "project %s not found", req.ID) err = errors.WithMessagef(ErrNotFound, "checklist %s not found", req.ID)
return nil, err return nil, err
} }
@ -213,12 +213,12 @@ func (repo *Repository) Read(ctx context.Context, claims auth.Claims, req Projec
return u, nil return u, nil
} }
// Create inserts a new project into the database. // Create inserts a new checklist into the database.
func (repo *Repository) Create(ctx context.Context, claims auth.Claims, req ProjectCreateRequest, now time.Time) (*Project, error) { func (repo *Repository) Create(ctx context.Context, claims auth.Claims, req ChecklistCreateRequest, now time.Time) (*Checklist, error) {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Create") span, ctx := tracer.StartSpanFromContext(ctx, "internal.checklist.Create")
defer span.Finish() defer span.Finish()
if claims.Audience != "" { if claims.Audience != "" {
// Admin users can update projects they have access to. // Admin users can update checklists they have access to.
if !claims.HasRole(auth.RoleAdmin) { if !claims.HasRole(auth.RoleAdmin) {
return nil, errors.WithStack(ErrForbidden) return nil, errors.WithStack(ErrForbidden)
} }
@ -253,11 +253,11 @@ func (repo *Repository) Create(ctx context.Context, claims auth.Claims, req Proj
// Postgres truncates times to milliseconds when storing. We and do the same // Postgres truncates times to milliseconds when storing. We and do the same
// here so the value we return is consistent with what we store. // here so the value we return is consistent with what we store.
now = now.Truncate(time.Millisecond) now = now.Truncate(time.Millisecond)
m := Project{ m := Checklist{
ID: uuid.NewRandom().String(), ID: uuid.NewRandom().String(),
AccountID: req.AccountID, AccountID: req.AccountID,
Name: req.Name, Name: req.Name,
Status: ProjectStatus_Active, Status: ChecklistStatus_Active,
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
} }
@ -268,7 +268,7 @@ func (repo *Repository) Create(ctx context.Context, claims auth.Claims, req Proj
// Build the insert SQL statement. // Build the insert SQL statement.
query := sqlbuilder.NewInsertBuilder() query := sqlbuilder.NewInsertBuilder()
query.InsertInto(projectTableName) query.InsertInto(checklistTableName)
query.Cols( query.Cols(
"id", "id",
"account_id", "account_id",
@ -295,16 +295,16 @@ func (repo *Repository) Create(ctx context.Context, claims auth.Claims, req Proj
_, err = repo.DbConn.ExecContext(ctx, sql, args...) _, err = repo.DbConn.ExecContext(ctx, sql, args...)
if err != nil { if err != nil {
err = errors.Wrapf(err, "query - %s", query.String()) err = errors.Wrapf(err, "query - %s", query.String())
err = errors.WithMessage(err, "create project failed") err = errors.WithMessage(err, "create checklist failed")
return nil, err return nil, err
} }
return &m, nil return &m, nil
} }
// Update replaces an project in the database. // Update replaces an checklist in the database.
func (repo *Repository) Update(ctx context.Context, claims auth.Claims, req ProjectUpdateRequest, now time.Time) error { func (repo *Repository) Update(ctx context.Context, claims auth.Claims, req ChecklistUpdateRequest, now time.Time) error {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Update") span, ctx := tracer.StartSpanFromContext(ctx, "internal.checklist.Update")
defer span.Finish() defer span.Finish()
// Validate the request. // Validate the request.
@ -314,8 +314,8 @@ func (repo *Repository) Update(ctx context.Context, claims auth.Claims, req Proj
return err return err
} }
// Ensure the claims can modify the project specified in the request. // Ensure the claims can modify the checklist specified in the request.
err = repo.CanModifyProject(ctx, claims, req.ID) err = repo.CanModifyChecklist(ctx, claims, req.ID)
if err != nil { if err != nil {
return err return err
} }
@ -332,7 +332,7 @@ func (repo *Repository) Update(ctx context.Context, claims auth.Claims, req Proj
now = now.Truncate(time.Millisecond) now = now.Truncate(time.Millisecond)
// Build the update SQL statement. // Build the update SQL statement.
query := sqlbuilder.NewUpdateBuilder() query := sqlbuilder.NewUpdateBuilder()
query.Update(projectTableName) query.Update(checklistTableName)
var fields []string var fields []string
if req.Name != nil { if req.Name != nil {
fields = append(fields, query.Assign("name", req.Name)) fields = append(fields, query.Assign("name", req.Name))
@ -357,16 +357,16 @@ func (repo *Repository) Update(ctx context.Context, claims auth.Claims, req Proj
_, err = repo.DbConn.ExecContext(ctx, sql, args...) _, err = repo.DbConn.ExecContext(ctx, sql, args...)
if err != nil { if err != nil {
err = errors.Wrapf(err, "query - %s", query.String()) err = errors.Wrapf(err, "query - %s", query.String())
err = errors.WithMessagef(err, "update project %s failed", req.ID) err = errors.WithMessagef(err, "update checklist %s failed", req.ID)
return err return err
} }
return nil return nil
} }
// Archive soft deleted the project from the database. // Archive soft deleted the checklist from the database.
func (repo *Repository) Archive(ctx context.Context, claims auth.Claims, req ProjectArchiveRequest, now time.Time) error { func (repo *Repository) Archive(ctx context.Context, claims auth.Claims, req ChecklistArchiveRequest, now time.Time) error {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Archive") span, ctx := tracer.StartSpanFromContext(ctx, "internal.checklist.Archive")
defer span.Finish() defer span.Finish()
// Validate the request. // Validate the request.
@ -376,8 +376,8 @@ func (repo *Repository) Archive(ctx context.Context, claims auth.Claims, req Pro
return err return err
} }
// Ensure the claims can modify the project specified in the request. // Ensure the claims can modify the checklist specified in the request.
err = repo.CanModifyProject(ctx, claims, req.ID) err = repo.CanModifyChecklist(ctx, claims, req.ID)
if err != nil { if err != nil {
return err return err
} }
@ -394,7 +394,7 @@ func (repo *Repository) Archive(ctx context.Context, claims auth.Claims, req Pro
now = now.Truncate(time.Millisecond) now = now.Truncate(time.Millisecond)
// Build the update SQL statement. // Build the update SQL statement.
query := sqlbuilder.NewUpdateBuilder() query := sqlbuilder.NewUpdateBuilder()
query.Update(projectTableName) query.Update(checklistTableName)
query.Set( query.Set(
query.Assign("archived_at", now), query.Assign("archived_at", now),
) )
@ -406,16 +406,16 @@ func (repo *Repository) Archive(ctx context.Context, claims auth.Claims, req Pro
_, err = repo.DbConn.ExecContext(ctx, sql, args...) _, err = repo.DbConn.ExecContext(ctx, sql, args...)
if err != nil { if err != nil {
err = errors.Wrapf(err, "query - %s", query.String()) err = errors.Wrapf(err, "query - %s", query.String())
err = errors.WithMessagef(err, "archive project %s failed", req.ID) err = errors.WithMessagef(err, "archive checklist %s failed", req.ID)
return err return err
} }
return nil return nil
} }
// Delete removes an project from the database. // Delete removes an checklist from the database.
func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, req ProjectDeleteRequest) error { func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, req ChecklistDeleteRequest) error {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Delete") span, ctx := tracer.StartSpanFromContext(ctx, "internal.checklist.Delete")
defer span.Finish() defer span.Finish()
// Validate the request. // Validate the request.
@ -425,15 +425,15 @@ func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, req Proj
return err return err
} }
// Ensure the claims can modify the project specified in the request. // Ensure the claims can modify the checklist specified in the request.
err = repo.CanModifyProject(ctx, claims, req.ID) err = repo.CanModifyChecklist(ctx, claims, req.ID)
if err != nil { if err != nil {
return err return err
} }
// Build the delete SQL statement. // Build the delete SQL statement.
query := sqlbuilder.NewDeleteBuilder() query := sqlbuilder.NewDeleteBuilder()
query.DeleteFrom(projectTableName) query.DeleteFrom(checklistTableName)
query.Where(query.Equal("id", req.ID)) query.Where(query.Equal("id", req.ID))
// Execute the query with the provided context. // Execute the query with the provided context.
sql, args := query.Build() sql, args := query.Build()
@ -441,7 +441,7 @@ func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, req Proj
_, err = repo.DbConn.ExecContext(ctx, sql, args...) _, err = repo.DbConn.ExecContext(ctx, sql, args...)
if err != nil { if err != nil {
err = errors.Wrapf(err, "query - %s", query.String()) err = errors.Wrapf(err, "query - %s", query.String())
err = errors.WithMessagef(err, "delete project %s failed", req.ID) err = errors.WithMessagef(err, "delete checklist %s failed", req.ID)
return err return err
} }

View File

@ -1,4 +1,4 @@
package project package checklist
import ( import (
"os" "os"
@ -37,7 +37,7 @@ func TestFindRequestQuery(t *testing.T) {
offset uint = 34 offset uint = 34
) )
req := ProjectFindRequest{ req := ChecklistFindRequest{
Where: "field1 = ? or field2 = ?", Where: "field1 = ? or field2 = ?",
Args: []interface{}{ Args: []interface{}{
"lee brown", "lee brown",
@ -53,7 +53,7 @@ func TestFindRequestQuery(t *testing.T) {
Offset: &offset, Offset: &offset,
} }
expected := "SELECT " + projectMapColumns + " FROM " + projectTableName + " WHERE (field1 = ? or field2 = ?) ORDER BY id asc, created_at desc LIMIT 12 OFFSET 34" expected := "SELECT " + checklistMapColumns + " FROM " + checklistTableName + " WHERE (field1 = ? or field2 = ?) ORDER BY id asc, created_at desc LIMIT 12 OFFSET 34"
res, args := findRequestQuery(req) res, args := findRequestQuery(req)
if diff := cmp.Diff(res.String(), expected); diff != "" { if diff := cmp.Diff(res.String(), expected); diff != "" {
t.Fatalf("\t%s\tExpected result query to match. Diff:\n%s", tests.Failed, diff) t.Fatalf("\t%s\tExpected result query to match. Diff:\n%s", tests.Failed, diff)
@ -101,9 +101,6 @@ func TestApplyClaimsSelect(t *testing.T) {
t.Logf("\t%s\tapplyClaimsSelect ok.", tests.Success) t.Logf("\t%s\tapplyClaimsSelect ok.", tests.Success)
} }
} }
} }
} }

View File

@ -0,0 +1,184 @@
package checklist
import (
"context"
"time"
"database/sql/driver"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
"github.com/pkg/errors"
"gopkg.in/go-playground/validator.v9"
)
// Repository defines the required dependencies for Checklist.
type Repository struct {
DbConn *sqlx.DB
}
// NewRepository creates a new Repository that defines dependencies for Checklist.
func NewRepository(db *sqlx.DB) *Repository {
return &Repository{
DbConn: db,
}
}
// Checklist represents a workflow.
type Checklist struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
AccountID string `json:"account_id" validate:"required,uuid" truss:"api-create"`
Name string `json:"name" validate:"required" example:"Rocket Launch"`
Status ChecklistStatus `json:"status" validate:"omitempty,oneof=active disabled" enums:"active,disabled" swaggertype:"string" example:"active"`
CreatedAt time.Time `json:"created_at" truss:"api-read"`
UpdatedAt time.Time `json:"updated_at" truss:"api-read"`
ArchivedAt *pq.NullTime `json:"archived_at,omitempty" truss:"api-hide"`
}
// ChecklistResponse represents a workflow that is returned for display.
type ChecklistResponse struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
AccountID string `json:"account_id" validate:"required,uuid" truss:"api-create" example:"c4653bf9-5978-48b7-89c5-95704aebb7e2"`
Name string `json:"name" validate:"required" example:"Rocket Launch"`
Status web.EnumResponse `json:"status"` // Status is enum with values [active, disabled].
CreatedAt web.TimeResponse `json:"created_at"` // CreatedAt contains multiple format options for display.
UpdatedAt web.TimeResponse `json:"updated_at"` // UpdatedAt contains multiple format options for display.
ArchivedAt *web.TimeResponse `json:"archived_at,omitempty"` // ArchivedAt contains multiple format options for display.
}
// Response transforms Checklist and ChecklistResponse that is used for display.
// Additional filtering by context values or translations could be applied.
func (m *Checklist) Response(ctx context.Context) *ChecklistResponse {
if m == nil {
return nil
}
r := &ChecklistResponse{
ID: m.ID,
AccountID: m.AccountID,
Name: m.Name,
Status: web.NewEnumResponse(ctx, m.Status, ChecklistStatus_ValuesInterface()...),
CreatedAt: web.NewTimeResponse(ctx, m.CreatedAt),
UpdatedAt: web.NewTimeResponse(ctx, m.UpdatedAt),
}
if m.ArchivedAt != nil && !m.ArchivedAt.Time.IsZero() {
at := web.NewTimeResponse(ctx, m.ArchivedAt.Time)
r.ArchivedAt = &at
}
return r
}
// Checklists a list of Checklists.
type Checklists []*Checklist
// Response transforms a list of Checklists to a list of ChecklistResponses.
func (m *Checklists) Response(ctx context.Context) []*ChecklistResponse {
var l []*ChecklistResponse
if m != nil && len(*m) > 0 {
for _, n := range *m {
l = append(l, n.Response(ctx))
}
}
return l
}
// ChecklistCreateRequest contains information needed to create a new Checklist.
type ChecklistCreateRequest struct {
AccountID string `json:"account_id" validate:"required,uuid" example:"c4653bf9-5978-48b7-89c5-95704aebb7e2"`
Name string `json:"name" validate:"required" example:"Rocket Launch"`
Status *ChecklistStatus `json:"status,omitempty" validate:"omitempty,oneof=active disabled" enums:"active,disabled" swaggertype:"string" example:"active"`
}
// ChecklistReadRequest defines the information needed to read a checklist.
type ChecklistReadRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
IncludeArchived bool `json:"include-archived" example:"false"`
}
// ChecklistUpdateRequest defines what information may be provided to modify an existing
// Checklist. All fields are optional so clients can send just the fields they want
// changed. It uses pointer fields so we can differentiate between a field that
// was not provided and a field that was provided as explicitly blank.
type ChecklistUpdateRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
Name *string `json:"name,omitempty" validate:"omitempty" example:"Rocket Launch to Moon"`
Status *ChecklistStatus `json:"status,omitempty" validate:"omitempty,oneof=active disabled" enums:"active,disabled" swaggertype:"string" example:"disabled"`
}
// ChecklistArchiveRequest defines the information needed to archive a checklist. This will archive (soft-delete) the
// existing database entry.
type ChecklistArchiveRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
}
// ChecklistDeleteRequest defines the information needed to delete a checklist.
type ChecklistDeleteRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
}
// ChecklistFindRequest defines the possible options to search for checklists. By default
// archived checklist will be excluded from response.
type ChecklistFindRequest struct {
Where string `json:"where" example:"name = ? and status = ?"`
Args []interface{} `json:"args" swaggertype:"array,string" example:"Moon Launch,active"`
Order []string `json:"order" example:"created_at desc"`
Limit *uint `json:"limit" example:"10"`
Offset *uint `json:"offset" example:"20"`
IncludeArchived bool `json:"include-archived" example:"false"`
}
// ChecklistStatus represents the status of checklist.
type ChecklistStatus string
// ChecklistStatus values define the status field of checklist.
const (
// ChecklistStatus_Active defines the status of active for checklist.
ChecklistStatus_Active ChecklistStatus = "active"
// ChecklistStatus_Disabled defines the status of disabled for checklist.
ChecklistStatus_Disabled ChecklistStatus = "disabled"
)
// ChecklistStatus_Values provides list of valid ChecklistStatus values.
var ChecklistStatus_Values = []ChecklistStatus{
ChecklistStatus_Active,
ChecklistStatus_Disabled,
}
// ChecklistStatus_ValuesInterface returns the ChecklistStatus options as a slice interface.
func ChecklistStatus_ValuesInterface() []interface{} {
var l []interface{}
for _, v := range ChecklistStatus_Values {
l = append(l, v.String())
}
return l
}
// Scan supports reading the ChecklistStatus value from the database.
func (s *ChecklistStatus) Scan(value interface{}) error {
asBytes, ok := value.([]byte)
if !ok {
return errors.New("Scan source is not []byte")
}
*s = ChecklistStatus(string(asBytes))
return nil
}
// Value converts the ChecklistStatus value to be stored in the database.
func (s ChecklistStatus) Value() (driver.Value, error) {
v := validator.New()
errs := v.Var(s, "required,oneof=active disabled")
if errs != nil {
return nil, errs
}
return string(s), nil
}
// String converts the ChecklistStatus value to a string.
func (s ChecklistStatus) String() string {
return string(s)
}

View File

@ -177,7 +177,7 @@ func S3ImgSrc(ctx context.Context, redisClient *redistrace.Client, s3UrlFormatte
Prefix: aws.String(s3Path), Prefix: aws.String(s3Path),
}) })
if err != nil { if err != nil {
return defaultSrc, errors.WithStack(err) return defaultSrc, errors.WithMessagef(err, "Failed to list objects for s3://%s/%s", s3Bucket, s3Path)
} }
// Loop through all the S3 objects and store by in map by // Loop through all the S3 objects and store by in map by

View File

@ -1,184 +0,0 @@
package project
import (
"context"
"time"
"database/sql/driver"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
"github.com/pkg/errors"
"gopkg.in/go-playground/validator.v9"
)
// Repository defines the required dependencies for Project.
type Repository struct {
DbConn *sqlx.DB
}
// NewRepository creates a new Repository that defines dependencies for Project.
func NewRepository(db *sqlx.DB) *Repository {
return &Repository{
DbConn: db,
}
}
// Project represents a workflow.
type Project struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
AccountID string `json:"account_id" validate:"required,uuid" truss:"api-create"`
Name string `json:"name" validate:"required" example:"Rocket Launch"`
Status ProjectStatus `json:"status" validate:"omitempty,oneof=active disabled" enums:"active,disabled" swaggertype:"string" example:"active"`
CreatedAt time.Time `json:"created_at" truss:"api-read"`
UpdatedAt time.Time `json:"updated_at" truss:"api-read"`
ArchivedAt *pq.NullTime `json:"archived_at,omitempty" truss:"api-hide"`
}
// ProjectResponse represents a workflow that is returned for display.
type ProjectResponse struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
AccountID string `json:"account_id" validate:"required,uuid" truss:"api-create" example:"c4653bf9-5978-48b7-89c5-95704aebb7e2"`
Name string `json:"name" validate:"required" example:"Rocket Launch"`
Status web.EnumResponse `json:"status"` // Status is enum with values [active, disabled].
CreatedAt web.TimeResponse `json:"created_at"` // CreatedAt contains multiple format options for display.
UpdatedAt web.TimeResponse `json:"updated_at"` // UpdatedAt contains multiple format options for display.
ArchivedAt *web.TimeResponse `json:"archived_at,omitempty"` // ArchivedAt contains multiple format options for display.
}
// Response transforms Project and ProjectResponse that is used for display.
// Additional filtering by context values or translations could be applied.
func (m *Project) Response(ctx context.Context) *ProjectResponse {
if m == nil {
return nil
}
r := &ProjectResponse{
ID: m.ID,
AccountID: m.AccountID,
Name: m.Name,
Status: web.NewEnumResponse(ctx, m.Status, ProjectStatus_ValuesInterface()...),
CreatedAt: web.NewTimeResponse(ctx, m.CreatedAt),
UpdatedAt: web.NewTimeResponse(ctx, m.UpdatedAt),
}
if m.ArchivedAt != nil && !m.ArchivedAt.Time.IsZero() {
at := web.NewTimeResponse(ctx, m.ArchivedAt.Time)
r.ArchivedAt = &at
}
return r
}
// Projects a list of Projects.
type Projects []*Project
// Response transforms a list of Projects to a list of ProjectResponses.
func (m *Projects) Response(ctx context.Context) []*ProjectResponse {
var l []*ProjectResponse
if m != nil && len(*m) > 0 {
for _, n := range *m {
l = append(l, n.Response(ctx))
}
}
return l
}
// ProjectCreateRequest contains information needed to create a new Project.
type ProjectCreateRequest struct {
AccountID string `json:"account_id" validate:"required,uuid" example:"c4653bf9-5978-48b7-89c5-95704aebb7e2"`
Name string `json:"name" validate:"required" example:"Rocket Launch"`
Status *ProjectStatus `json:"status,omitempty" validate:"omitempty,oneof=active disabled" enums:"active,disabled" swaggertype:"string" example:"active"`
}
// ProjectReadRequest defines the information needed to read a project.
type ProjectReadRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
IncludeArchived bool `json:"include-archived" example:"false"`
}
// ProjectUpdateRequest defines what information may be provided to modify an existing
// Project. All fields are optional so clients can send just the fields they want
// changed. It uses pointer fields so we can differentiate between a field that
// was not provided and a field that was provided as explicitly blank.
type ProjectUpdateRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
Name *string `json:"name,omitempty" validate:"omitempty" example:"Rocket Launch to Moon"`
Status *ProjectStatus `json:"status,omitempty" validate:"omitempty,oneof=active disabled" enums:"active,disabled" swaggertype:"string" example:"disabled"`
}
// ProjectArchiveRequest defines the information needed to archive a project. This will archive (soft-delete) the
// existing database entry.
type ProjectArchiveRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
}
// ProjectDeleteRequest defines the information needed to delete a project.
type ProjectDeleteRequest struct {
ID string `json:"id" validate:"required,uuid" example:"985f1746-1d9f-459f-a2d9-fc53ece5ae86"`
}
// ProjectFindRequest defines the possible options to search for projects. By default
// archived project will be excluded from response.
type ProjectFindRequest struct {
Where string `json:"where" example:"name = ? and status = ?"`
Args []interface{} `json:"args" swaggertype:"array,string" example:"Moon Launch,active"`
Order []string `json:"order" example:"created_at desc"`
Limit *uint `json:"limit" example:"10"`
Offset *uint `json:"offset" example:"20"`
IncludeArchived bool `json:"include-archived" example:"false"`
}
// ProjectStatus represents the status of project.
type ProjectStatus string
// ProjectStatus values define the status field of project.
const (
// ProjectStatus_Active defines the status of active for project.
ProjectStatus_Active ProjectStatus = "active"
// ProjectStatus_Disabled defines the status of disabled for project.
ProjectStatus_Disabled ProjectStatus = "disabled"
)
// ProjectStatus_Values provides list of valid ProjectStatus values.
var ProjectStatus_Values = []ProjectStatus{
ProjectStatus_Active,
ProjectStatus_Disabled,
}
// ProjectStatus_ValuesInterface returns the ProjectStatus options as a slice interface.
func ProjectStatus_ValuesInterface() []interface{} {
var l []interface{}
for _, v := range ProjectStatus_Values {
l = append(l, v.String())
}
return l
}
// Scan supports reading the ProjectStatus value from the database.
func (s *ProjectStatus) Scan(value interface{}) error {
asBytes, ok := value.([]byte)
if !ok {
return errors.New("Scan source is not []byte")
}
*s = ProjectStatus(string(asBytes))
return nil
}
// Value converts the ProjectStatus value to be stored in the database.
func (s ProjectStatus) Value() (driver.Value, error) {
v := validator.New()
errs := v.Var(s, "required,oneof=active disabled")
if errs != nil {
return nil, errs
}
return string(s), nil
}
// String converts the ProjectStatus value to a string.
func (s ProjectStatus) String() string {
return string(s)
}

View File

@ -662,6 +662,21 @@ func migrationList(ctx context.Context, db *sqlx.DB, log *log.Logger, isUnittest
return nil return nil
}, },
}, },
// Remove default value for users.timezone.
{
ID: "20200118-01",
Migrate: func(tx *sql.Tx) error {
q1 := `ALTER TABLE projects RENAME TO checklists`
if _, err := tx.Exec(q1); err != nil {
return errors.Wrapf(err, "Query failed %s", q1)
}
return nil
},
Rollback: func(tx *sql.Tx) error {
return nil
},
},
} }
} }

View File

@ -10,13 +10,13 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type ProjectRoute struct { type WebRoute struct {
webAppUrl url.URL webAppUrl url.URL
webApiUrl url.URL webApiUrl url.URL
} }
func New(apiBaseUrl, appBaseUrl string) (ProjectRoute, error) { func New(apiBaseUrl, appBaseUrl string) (WebRoute, error) {
var r ProjectRoute var r WebRoute
apiUrl, err := url.Parse(apiBaseUrl) apiUrl, err := url.Parse(apiBaseUrl)
if err != nil { if err != nil {
@ -33,37 +33,37 @@ func New(apiBaseUrl, appBaseUrl string) (ProjectRoute, error) {
return r, nil return r, nil
} }
func (r ProjectRoute) WebAppUrl(urlPath string) string { func (r WebRoute) WebAppUrl(urlPath string) string {
u := r.webAppUrl u := r.webAppUrl
u.Path = urlPath u.Path = urlPath
return u.String() return u.String()
} }
func (r ProjectRoute) WebApiUrl(urlPath string) string { func (r WebRoute) WebApiUrl(urlPath string) string {
u := r.webApiUrl u := r.webApiUrl
u.Path = urlPath u.Path = urlPath
return u.String() return u.String()
} }
func (r ProjectRoute) UserResetPassword(resetHash string) string { func (r WebRoute) UserResetPassword(resetHash string) string {
u := r.webAppUrl u := r.webAppUrl
u.Path = "/user/reset-password/" + resetHash u.Path = "/user/reset-password/" + resetHash
return u.String() return u.String()
} }
func (r ProjectRoute) UserInviteAccept(inviteHash string) string { func (r WebRoute) UserInviteAccept(inviteHash string) string {
u := r.webAppUrl u := r.webAppUrl
u.Path = "/users/invite/" + inviteHash u.Path = "/users/invite/" + inviteHash
return u.String() return u.String()
} }
func (r ProjectRoute) ApiDocs() string { func (r WebRoute) ApiDocs() string {
u := r.webApiUrl u := r.webApiUrl
u.Path = "/docs" u.Path = "/docs"
return u.String() return u.String()
} }
func (r ProjectRoute) ApiDocsJson(internal bool) string { func (r WebRoute) ApiDocsJson(internal bool) string {
u := r.webApiUrl u := r.webApiUrl
if ev := os.Getenv("USE_NETWORK_ALIAS"); ev != "" { if ev := os.Getenv("USE_NETWORK_ALIAS"); ev != "" {