You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-17 00:17:59 +02:00
issue #28 - renamed project to checklist
This commit is contained in:
@ -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"
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
@ -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>
|
@ -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>
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
184
internal/checklist/models.go
Normal file
184
internal/checklist/models.go
Normal 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)
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 != "" {
|
||||||
|
Reference in New Issue
Block a user