From ad6cf16e4b3241e1b7158bcc7675dbd19d0ff691 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 15 Oct 2018 14:46:35 +0000 Subject: [PATCH] add(crm): import implementation for pages --- crm/docs/README.md | 97 ++++++++++- crm/docs/src/spec.json | 86 +++++++++- crm/docs/src/spec/module.json | 12 +- crm/docs/src/spec/page.json | 152 +++++++++++++++++ crm/repository/page.go | 66 ++++++++ crm/repository/page_test.go | 71 ++++++++ crm/rest/field.go | 5 +- crm/rest/handlers/page.go | 97 +++++++++++ crm/rest/module.go | 9 +- crm/rest/page.go | 58 +++++++ crm/rest/request/module.go | 30 ++-- crm/rest/request/page.go | 300 ++++++++++++++++++++++++++++++++++ crm/rest/router.go | 6 +- crm/service/page.go | 62 +++++++ crm/types/types.go | 27 +++ 15 files changed, 1035 insertions(+), 43 deletions(-) create mode 100644 crm/docs/src/spec/page.json create mode 100644 crm/repository/page.go create mode 100644 crm/repository/page_test.go create mode 100644 crm/rest/handlers/page.go create mode 100644 crm/rest/page.go create mode 100644 crm/rest/request/page.go create mode 100644 crm/service/page.go diff --git a/crm/docs/README.md b/crm/docs/README.md index cb8a27811..d5330834a 100644 --- a/crm/docs/README.md +++ b/crm/docs/README.md @@ -32,6 +32,93 @@ CRM input field definitions +# Pages + +CRM module pages + +## List available pages + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/page/` | HTTP/S | GET | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | + +## Create page + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/page/` | HTTP/S | POST | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| selfID | uint64 | POST | Parent Page ID | N/A | NO | +| moduleID | uint64 | POST | Module ID (optional) | N/A | NO | +| title | string | POST | Title | N/A | YES | +| description | string | POST | Description | N/A | NO | +| visible | bool | POST | Visible in navigation | N/A | NO | +| blocks | types.JSONText | POST | Blocks JSON | N/A | YES | + +## Get page details + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/page/{id}` | HTTP/S | GET | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| id | uint64 | PATH | Page ID | N/A | YES | + +## Create page + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/page/{id}` | HTTP/S | POST | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| id | uint64 | PATH | Page ID | N/A | YES | +| selfID | uint64 | POST | Parent Page ID | N/A | NO | +| moduleID | uint64 | POST | Module ID (optional) | N/A | NO | +| title | string | POST | Title | N/A | YES | +| description | string | POST | Description | N/A | NO | +| visible | bool | POST | Visible in navigation | N/A | NO | +| blocks | types.JSONText | POST | Blocks JSON | N/A | YES | + +## Delete page + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/page/{id}` | HTTP/S | Delete | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| id | uint64 | PATH | Page ID | N/A | YES | + + + + # Modules CRM module definitions @@ -121,7 +208,7 @@ CRM module definitions | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | -| module | uint64 | PATH | Module ID | N/A | YES | +| moduleID | uint64 | PATH | Module ID | N/A | YES | ## List/read contents from module section @@ -135,7 +222,7 @@ CRM module definitions | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | -| module | uint64 | PATH | Module ID | N/A | YES | +| moduleID | uint64 | PATH | Module ID | N/A | YES | | fields | types.JSONText | POST | Content JSON | N/A | YES | ## Read contents by ID from module section @@ -150,7 +237,7 @@ CRM module definitions | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | -| module | uint64 | PATH | Module ID | N/A | YES | +| moduleID | uint64 | PATH | Module ID | N/A | YES | | id | uint64 | PATH | Content ID | N/A | YES | ## Add/update contents in module section @@ -165,7 +252,7 @@ CRM module definitions | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | -| module | uint64 | PATH | Module ID | N/A | YES | +| moduleID | uint64 | PATH | Module ID | N/A | YES | | id | uint64 | PATH | Content ID | N/A | YES | | fields | types.JSONText | POST | Content JSON | N/A | YES | @@ -181,5 +268,5 @@ CRM module definitions | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | -| module | uint64 | PATH | Module ID | N/A | YES | +| moduleID | uint64 | PATH | Module ID | N/A | YES | | id | uint64 | PATH | Content ID | N/A | YES | \ No newline at end of file diff --git a/crm/docs/src/spec.json b/crm/docs/src/spec.json index e91183fac..83fd28360 100644 --- a/crm/docs/src/spec.json +++ b/crm/docs/src/spec.json @@ -36,6 +36,80 @@ } ] }, + { + "title": "Pages", + "description": "CRM module pages", + "package": "crm", + "entrypoint": "page", + "path": "/page", + "authentication": [], + "struct": [], + "apis": [ + { + "name": "list", + "method": "GET", + "path": "/", + "title": "List available pages" + }, + { + "name": "create", + "method": "POST", + "title": "Create page", + "path": "/", + "parameters": { + "post": [ + { "type": "uint64", "name": "selfID", "required": false, "title": "Parent Page ID" }, + { "type": "uint64", "name": "moduleID", "required": false, "title": "Module ID (optional)" }, + { "type": "string", "name": "title", "required": true, "title": "Title" }, + { "type": "string", "name": "description", "required": false, "title": "Description" }, + { "type": "bool", "name": "visible", "required": false, "title": "Visible in navigation" }, + { "type": "types.JSONText", "name": "blocks", "required": true, "title": "Blocks JSON" } + ] + } + }, + { + "name": "read", + "path": "/{id}", + "method": "GET", + "title": "Get page details", + "parameters": { + "path": [ + { "type": "uint64", "name": "id", "required": true, "title": "Page ID" } + ] + } + }, + { + "name": "edit", + "method": "POST", + "title": "Create page", + "path": "/{id}", + "parameters": { + "path": [ + { "type": "uint64", "name": "id", "required": true, "title": "Page ID" } + ], + "post": [ + { "type": "uint64", "name": "selfID", "required": false, "title": "Parent Page ID" }, + { "type": "uint64", "name": "moduleID", "required": false, "title": "Module ID (optional)" }, + { "type": "string", "name": "title", "required": true, "title": "Title" }, + { "type": "string", "name": "description", "required": false, "title": "Description" }, + { "type": "bool", "name": "visible", "required": false, "title": "Visible in navigation" }, + { "type": "types.JSONText", "name": "blocks", "required": true, "title": "Blocks JSON" } + ] + } + }, + { + "name": "delete", + "path": "/{id}", + "method": "Delete", + "title": "Delete page", + "parameters": { + "path": [ + { "type": "uint64", "name": "id", "required": true, "title": "Page ID" } + ] + } + } + ] + }, { "title": "Modules", "description": "CRM module definitions", @@ -72,7 +146,7 @@ ], "fields": [ { "name": "ID", "type": "uint64" }, - { "name": "ModuleID", "type": "uint64" }, + { "name": "moduleID", "type": "uint64" }, { "name": "Fields", "type": "types.JSONText", "db": "json" } ] } @@ -145,7 +219,7 @@ "path": "/{module}/content", "parameters": { "path": [ - { "type": "uint64", "name": "module", "required": true, "title": "Module ID" } + { "type": "uint64", "name": "moduleID", "required": true, "title": "Module ID" } ] } }, @@ -156,7 +230,7 @@ "path": "/{module}/content", "parameters": { "path": [ - { "type": "uint64", "name": "module", "required": true, "title": "Module ID" } + { "type": "uint64", "name": "moduleID", "required": true, "title": "Module ID" } ], "post": [ { "type": "types.JSONText", "name": "fields", "required": true, "title": "Content JSON" } @@ -170,7 +244,7 @@ "path": "/{module}/content/{id}", "parameters": { "path": [ - { "type": "uint64", "name": "module", "required": true, "title": "Module ID" }, + { "type": "uint64", "name": "moduleID", "required": true, "title": "Module ID" }, { "type": "uint64", "name": "id", "required": true, "title": "Content ID" } ] } @@ -182,7 +256,7 @@ "path": "/{module}/content/{id}", "parameters": { "path": [ - { "type": "uint64", "name": "module", "required": true, "title": "Module ID" }, + { "type": "uint64", "name": "moduleID", "required": true, "title": "Module ID" }, { "type": "uint64", "name": "id", "required": true, "title": "Content ID" } ], "post": [ @@ -197,7 +271,7 @@ "path": "/{module}/content/{id}", "parameters": { "path": [ - { "type": "uint64", "name": "module", "required": true, "title": "Module ID" }, + { "type": "uint64", "name": "moduleID", "required": true, "title": "Module ID" }, { "type": "uint64", "name": "id", "required": true, "title": "Content ID" } ] } diff --git a/crm/docs/src/spec/module.json b/crm/docs/src/spec/module.json index 20df01c26..e92db1bf8 100644 --- a/crm/docs/src/spec/module.json +++ b/crm/docs/src/spec/module.json @@ -58,7 +58,7 @@ "type": "uint64" }, { - "name": "ModuleID", + "name": "moduleID", "type": "uint64" }, { @@ -186,7 +186,7 @@ "Parameters": { "path": [ { - "name": "module", + "name": "moduleID", "required": true, "title": "Module ID", "type": "uint64" @@ -202,7 +202,7 @@ "Parameters": { "path": [ { - "name": "module", + "name": "moduleID", "required": true, "title": "Module ID", "type": "uint64" @@ -226,7 +226,7 @@ "Parameters": { "path": [ { - "name": "module", + "name": "moduleID", "required": true, "title": "Module ID", "type": "uint64" @@ -248,7 +248,7 @@ "Parameters": { "path": [ { - "name": "module", + "name": "moduleID", "required": true, "title": "Module ID", "type": "uint64" @@ -278,7 +278,7 @@ "Parameters": { "path": [ { - "name": "module", + "name": "moduleID", "required": true, "title": "Module ID", "type": "uint64" diff --git a/crm/docs/src/spec/page.json b/crm/docs/src/spec/page.json new file mode 100644 index 000000000..55024a998 --- /dev/null +++ b/crm/docs/src/spec/page.json @@ -0,0 +1,152 @@ +{ + "Title": "Pages", + "Description": "CRM module pages", + "Package": "crm", + "Interface": "Page", + "Struct": [], + "Parameters": null, + "Protocol": "", + "Authentication": [], + "Path": "/page", + "APIs": [ + { + "Name": "list", + "Method": "GET", + "Title": "List available pages", + "Path": "/", + "Parameters": null + }, + { + "Name": "create", + "Method": "POST", + "Title": "Create page", + "Path": "/", + "Parameters": { + "post": [ + { + "name": "selfID", + "required": false, + "title": "Parent Page ID", + "type": "uint64" + }, + { + "name": "moduleID", + "required": false, + "title": "Module ID (optional)", + "type": "uint64" + }, + { + "name": "title", + "required": true, + "title": "Title", + "type": "string" + }, + { + "name": "description", + "required": false, + "title": "Description", + "type": "string" + }, + { + "name": "visible", + "required": false, + "title": "Visible in navigation", + "type": "bool" + }, + { + "name": "blocks", + "required": true, + "title": "Blocks JSON", + "type": "types.JSONText" + } + ] + } + }, + { + "Name": "read", + "Method": "GET", + "Title": "Get page details", + "Path": "/{id}", + "Parameters": { + "path": [ + { + "name": "id", + "required": true, + "title": "Page ID", + "type": "uint64" + } + ] + } + }, + { + "Name": "edit", + "Method": "POST", + "Title": "Create page", + "Path": "/{id}", + "Parameters": { + "path": [ + { + "name": "id", + "required": true, + "title": "Page ID", + "type": "uint64" + } + ], + "post": [ + { + "name": "selfID", + "required": false, + "title": "Parent Page ID", + "type": "uint64" + }, + { + "name": "moduleID", + "required": false, + "title": "Module ID (optional)", + "type": "uint64" + }, + { + "name": "title", + "required": true, + "title": "Title", + "type": "string" + }, + { + "name": "description", + "required": false, + "title": "Description", + "type": "string" + }, + { + "name": "visible", + "required": false, + "title": "Visible in navigation", + "type": "bool" + }, + { + "name": "blocks", + "required": true, + "title": "Blocks JSON", + "type": "types.JSONText" + } + ] + } + }, + { + "Name": "delete", + "Method": "Delete", + "Title": "Delete page", + "Path": "/{id}", + "Parameters": { + "path": [ + { + "name": "id", + "required": true, + "title": "Page ID", + "type": "uint64" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/crm/repository/page.go b/crm/repository/page.go new file mode 100644 index 000000000..e25af5a14 --- /dev/null +++ b/crm/repository/page.go @@ -0,0 +1,66 @@ +package repository + +import ( + "context" + + "github.com/titpetric/factory" + + "github.com/crusttech/crust/crm/types" +) + +type ( + PageRepository interface { + With(ctx context.Context, db *factory.DB) PageRepository + + Find() ([]*types.Page, error) + FindByID(id uint64) (*types.Page, error) + FindByModuleID(id uint64) (*types.Page, error) + + Create(mod *types.Page) (*types.Page, error) + Update(mod *types.Page) (*types.Page, error) + DeleteByID(id uint64) error + } + + page struct { + *repository + } +) + +func Page(ctx context.Context, db *factory.DB) PageRepository { + return (&page{}).With(ctx, db) +} + +func (r *page) With(ctx context.Context, db *factory.DB) PageRepository { + return &page{ + repository: r.repository.With(ctx, db), + } +} + +func (r *page) FindByID(id uint64) (*types.Page, error) { + mod := &types.Page{} + return mod, r.db().Get(mod, "SELECT * FROM crm_page WHERE id=?", id) +} + +func (r *page) FindByModuleID(id uint64) (*types.Page, error) { + mod := &types.Page{} + return mod, r.db().Get(mod, "SELECT * FROM crm_page WHERE module_id=?", id) +} + +func (r *page) Find() ([]*types.Page, error) { + mod := make([]*types.Page, 0) + return mod, r.db().Select(&mod, "SELECT * FROM crm_page ORDER BY id ASC") +} + +func (r *page) Create(mod *types.Page) (*types.Page, error) { + mod.ID = factory.Sonyflake.NextID() + return mod, r.db().Insert("crm_page", mod) +} + +func (r *page) Update(mod *types.Page) (*types.Page, error) { + return mod, r.db().Replace("crm_page", mod) +} + +func (r *page) DeleteByID(id uint64) error { + _, err := r.db().Exec("DELETE FROM crm_page WHERE id=?", id) + return err +} diff --git a/crm/repository/page_test.go b/crm/repository/page_test.go new file mode 100644 index 000000000..00c759363 --- /dev/null +++ b/crm/repository/page_test.go @@ -0,0 +1,71 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/crm/types" + "testing" +) + +func TestPage(t *testing.T) { + repository := Page(context.TODO(), nil).With(context.Background(), nil) + + // the page object we're working with + page := &types.Page{ + Name: "Test", + } + (&page.Blocks).Scan([]byte("[]")) + + prevPageCount := 0 + + { + // create page + m, err := repository.Create(page) + assert(t, err == nil, "Error when creating page: %+v", err) + assert(t, m.ID > 0, "Expected auto generated ID") + + // fetch created page + { + ms, err := repository.FindByID(m.ID) + assert(t, err == nil, "Error when retrieving page by id: %+v", err) + assert(t, ms.ID == m.ID, "Expected ID from database to match, %d != %d", m.ID, ms.ID) + assert(t, ms.Name == m.Name, "Expected Name from database to match, %s != %s", m.Name, ms.Name) + } + + // update created page + { + m.Name = "Updated test" + _, err := repository.Update(m) + assert(t, err == nil, "Error when updating page, %+v", err) + } + + // re-fetch page + { + ms, err := repository.FindByID(m.ID) + assert(t, err == nil, "Error when retrieving page by id: %+v", err) + assert(t, ms.ID == m.ID, "Expected ID from database to match, %d != %d", m.ID, ms.ID) + assert(t, ms.Name == m.Name, "Expected Name from database to match, %s != %s", m.Name, ms.Name) + } + + // fetch all pages + { + ms, err := repository.Find() + assert(t, err == nil, "Error when retrieving pages: %+v", err) + assert(t, len(ms) >= 1, "Expected at least one page, got %d", len(ms)) + prevPageCount = len(ms) + } + + // re-fetch page + { + err := repository.DeleteByID(m.ID) + assert(t, err == nil, "Error when deleting page by id: %+v", err) + } + + // fetch all pages + { + ms, err := repository.Find() + assert(t, err == nil, "Error when retrieving pages: %+v", err) + assert(t, len(ms) < prevPageCount, "Expected pages count to decrease after deletion, %d < %d", len(ms), prevPageCount) + } + } + +} diff --git a/crm/rest/field.go b/crm/rest/field.go index 1d33b4f72..57738fa39 100644 --- a/crm/rest/field.go +++ b/crm/rest/field.go @@ -1,16 +1,13 @@ package rest import ( - "github.com/pkg/errors" - "context" + "github.com/crusttech/crust/crm/rest/request" "github.com/crusttech/crust/crm/service" "github.com/crusttech/crust/crm/types" ) -var _ = errors.Wrap - type ( Field struct { field service.FieldService diff --git a/crm/rest/handlers/page.go b/crm/rest/handlers/page.go new file mode 100644 index 000000000..88775623d --- /dev/null +++ b/crm/rest/handlers/page.go @@ -0,0 +1,97 @@ +package handlers + +/* + Hello! This file is auto-generated from `docs/src/spec.json`. + + For development: + In order to update the generated files, edit this file under the location, + add your struct fields, imports, API definitions and whatever you want, and: + + 1. run [spec](https://github.com/titpetric/spec) in the same folder, + 2. run `./_gen.php` in this folder. + + You may edit `page.go`, `page.util.go` or `page_test.go` to + implement your API calls, helper functions and tests. The file `page.go` + is only generated the first time, and will not be overwritten if it exists. +*/ + +import ( + "context" + "github.com/go-chi/chi" + "net/http" + + "github.com/titpetric/factory/resputil" + + "github.com/crusttech/crust/crm/rest/request" +) + +// Internal API interface +type PageAPI interface { + List(context.Context, *request.PageList) (interface{}, error) + Create(context.Context, *request.PageCreate) (interface{}, error) + Read(context.Context, *request.PageRead) (interface{}, error) + Edit(context.Context, *request.PageEdit) (interface{}, error) + Delete(context.Context, *request.PageDelete) (interface{}, error) +} + +// HTTP API interface +type Page struct { + List func(http.ResponseWriter, *http.Request) + Create func(http.ResponseWriter, *http.Request) + Read func(http.ResponseWriter, *http.Request) + Edit func(http.ResponseWriter, *http.Request) + Delete func(http.ResponseWriter, *http.Request) +} + +func NewPage(ph PageAPI) *Page { + return &Page{ + List: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewPageList() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return ph.List(r.Context(), params) + }) + }, + Create: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewPageCreate() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return ph.Create(r.Context(), params) + }) + }, + Read: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewPageRead() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return ph.Read(r.Context(), params) + }) + }, + Edit: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewPageEdit() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return ph.Edit(r.Context(), params) + }) + }, + Delete: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewPageDelete() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return ph.Delete(r.Context(), params) + }) + }, + } +} + +func (ph *Page) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) { + r.Group(func(r chi.Router) { + r.Use(middlewares...) + r.Route("/page", func(r chi.Router) { + r.Get("/", ph.List) + r.Post("/", ph.Create) + r.Get("/{id}", ph.Read) + r.Post("/{id}", ph.Edit) + r.Delete("/{id}", ph.Delete) + }) + }) +} diff --git a/crm/rest/module.go b/crm/rest/module.go index 9ba67c04a..5615c67b7 100644 --- a/crm/rest/module.go +++ b/crm/rest/module.go @@ -1,18 +1,15 @@ package rest import ( - "github.com/pkg/errors" + "context" "github.com/titpetric/factory/resputil" - "context" "github.com/crusttech/crust/crm/rest/request" "github.com/crusttech/crust/crm/service" "github.com/crusttech/crust/crm/types" ) -var _ = errors.Wrap - type ( Module struct { module service.ModuleService @@ -63,7 +60,7 @@ func (s *Module) ContentRead(ctx context.Context, r *request.ModuleContentRead) func (s *Module) ContentCreate(ctx context.Context, r *request.ModuleContentCreate) (interface{}, error) { item := &types.Content{ - ModuleID: r.Module, + ModuleID: r.ModuleID, Fields: r.Fields, } return s.content.With(ctx).Create(item) @@ -72,7 +69,7 @@ func (s *Module) ContentCreate(ctx context.Context, r *request.ModuleContentCrea func (s *Module) ContentEdit(ctx context.Context, r *request.ModuleContentEdit) (interface{}, error) { item := &types.Content{ ID: r.ID, - ModuleID: r.Module, + ModuleID: r.ModuleID, Fields: r.Fields, } return s.content.With(ctx).Update(item) diff --git a/crm/rest/page.go b/crm/rest/page.go new file mode 100644 index 000000000..e5628f47f --- /dev/null +++ b/crm/rest/page.go @@ -0,0 +1,58 @@ +package rest + +import ( + "context" + + "github.com/titpetric/factory/resputil" + + "github.com/crusttech/crust/crm/rest/request" + "github.com/crusttech/crust/crm/service" + "github.com/crusttech/crust/crm/types" +) + +type ( + Page struct { + page service.PageService + } +) + +func (Page) New(pageSvc service.PageService) *Page { + return &Page{pageSvc} +} + +func (ctrl *Page) List(ctx context.Context, r *request.PageList) (interface{}, error) { + return ctrl.page.With(ctx).Find() +} + +func (ctrl *Page) Create(ctx context.Context, r *request.PageCreate) (interface{}, error) { + p := &types.Page{ + SelfID: r.SelfID, + ModuleID: r.ModuleID, + Title: r.Title, + Description: r.Description, + Blocks: r.Blocks, + Visible: r.Visible, + } + return ctrl.page.With(ctx).Create(p) +} + +func (ctrl *Page) Read(ctx context.Context, r *request.PageRead) (interface{}, error) { + return ctrl.page.With(ctx).FindByID(r.ID) +} + +func (ctrl *Page) Edit(ctx context.Context, r *request.PageEdit) (interface{}, error) { + p := &types.Page{ + ID: r.ID, + SelfID: r.SelfID, + ModuleID: r.ModuleID, + Title: r.Title, + Description: r.Description, + Blocks: r.Blocks, + Visible: r.Visible, + } + return ctrl.page.With(ctx).Update(p) +} + +func (ctrl *Page) Delete(ctx context.Context, r *request.PageDelete) (interface{}, error) { + return resputil.OK(), ctrl.page.With(ctx).DeleteByID(r.ID) +} diff --git a/crm/rest/request/module.go b/crm/rest/request/module.go index 5a0dcd994..54eba451a 100644 --- a/crm/rest/request/module.go +++ b/crm/rest/request/module.go @@ -267,7 +267,7 @@ var _ RequestFiller = NewModuleDelete() // Module content/list request parameters type ModuleContentList struct { - Module uint64 + ModuleID uint64 } func NewModuleContentList() *ModuleContentList { @@ -300,7 +300,7 @@ func (m *ModuleContentList) Fill(r *http.Request) error { post[name] = string(param[0]) } - m.Module = parseUInt64(chi.URLParam(r, "module")) + m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) return err } @@ -309,8 +309,8 @@ var _ RequestFiller = NewModuleContentList() // Module content/create request parameters type ModuleContentCreate struct { - Module uint64 - Fields types.JSONText + ModuleID uint64 + Fields types.JSONText } func NewModuleContentCreate() *ModuleContentCreate { @@ -343,7 +343,7 @@ func (m *ModuleContentCreate) Fill(r *http.Request) error { post[name] = string(param[0]) } - m.Module = parseUInt64(chi.URLParam(r, "module")) + m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) if val, ok := post["fields"]; ok { if m.Fields, err = parseJSONText(val); err != nil { @@ -358,8 +358,8 @@ var _ RequestFiller = NewModuleContentCreate() // Module content/read request parameters type ModuleContentRead struct { - Module uint64 - ID uint64 + ModuleID uint64 + ID uint64 } func NewModuleContentRead() *ModuleContentRead { @@ -392,7 +392,7 @@ func (m *ModuleContentRead) Fill(r *http.Request) error { post[name] = string(param[0]) } - m.Module = parseUInt64(chi.URLParam(r, "module")) + m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) m.ID = parseUInt64(chi.URLParam(r, "id")) return err @@ -402,9 +402,9 @@ var _ RequestFiller = NewModuleContentRead() // Module content/edit request parameters type ModuleContentEdit struct { - Module uint64 - ID uint64 - Fields types.JSONText + ModuleID uint64 + ID uint64 + Fields types.JSONText } func NewModuleContentEdit() *ModuleContentEdit { @@ -437,7 +437,7 @@ func (m *ModuleContentEdit) Fill(r *http.Request) error { post[name] = string(param[0]) } - m.Module = parseUInt64(chi.URLParam(r, "module")) + m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) m.ID = parseUInt64(chi.URLParam(r, "id")) if val, ok := post["fields"]; ok { @@ -453,8 +453,8 @@ var _ RequestFiller = NewModuleContentEdit() // Module content/delete request parameters type ModuleContentDelete struct { - Module uint64 - ID uint64 + ModuleID uint64 + ID uint64 } func NewModuleContentDelete() *ModuleContentDelete { @@ -487,7 +487,7 @@ func (m *ModuleContentDelete) Fill(r *http.Request) error { post[name] = string(param[0]) } - m.Module = parseUInt64(chi.URLParam(r, "module")) + m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) m.ID = parseUInt64(chi.URLParam(r, "id")) return err diff --git a/crm/rest/request/page.go b/crm/rest/request/page.go new file mode 100644 index 000000000..464d2cf9e --- /dev/null +++ b/crm/rest/request/page.go @@ -0,0 +1,300 @@ +package request + +/* + Hello! This file is auto-generated from `docs/src/spec.json`. + + For development: + In order to update the generated files, edit this file under the location, + add your struct fields, imports, API definitions and whatever you want, and: + + 1. run [spec](https://github.com/titpetric/spec) in the same folder, + 2. run `./_gen.php` in this folder. + + You may edit `page.go`, `page.util.go` or `page_test.go` to + implement your API calls, helper functions and tests. The file `page.go` + is only generated the first time, and will not be overwritten if it exists. +*/ + +import ( + "encoding/json" + "github.com/go-chi/chi" + "github.com/jmoiron/sqlx/types" + "github.com/pkg/errors" + "io" + "mime/multipart" + "net/http" + "strings" +) + +var _ = chi.URLParam +var _ = types.JSONText{} +var _ = multipart.FileHeader{} + +// Page list request parameters +type PageList struct { +} + +func NewPageList() *PageList { + return &PageList{} +} + +func (p *PageList) Fill(r *http.Request) error { + var err error + + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(p) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + r.ParseForm() + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + return err +} + +var _ RequestFiller = NewPageList() + +// Page create request parameters +type PageCreate struct { + SelfID uint64 + ModuleID uint64 + Title string + Description string + Visible bool + Blocks types.JSONText +} + +func NewPageCreate() *PageCreate { + return &PageCreate{} +} + +func (p *PageCreate) Fill(r *http.Request) error { + var err error + + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(p) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + r.ParseForm() + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + if val, ok := post["selfID"]; ok { + + p.SelfID = parseUInt64(val) + } + if val, ok := post["moduleID"]; ok { + + p.ModuleID = parseUInt64(val) + } + if val, ok := post["title"]; ok { + + p.Title = val + } + if val, ok := post["description"]; ok { + + p.Description = val + } + if val, ok := post["visible"]; ok { + + p.Visible = parseBool(val) + } + if val, ok := post["blocks"]; ok { + + if p.Blocks, err = parseJSONText(val); err != nil { + return err + } + } + + return err +} + +var _ RequestFiller = NewPageCreate() + +// Page read request parameters +type PageRead struct { + ID uint64 +} + +func NewPageRead() *PageRead { + return &PageRead{} +} + +func (p *PageRead) Fill(r *http.Request) error { + var err error + + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(p) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + r.ParseForm() + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + p.ID = parseUInt64(chi.URLParam(r, "id")) + + return err +} + +var _ RequestFiller = NewPageRead() + +// Page edit request parameters +type PageEdit struct { + ID uint64 + SelfID uint64 + ModuleID uint64 + Title string + Description string + Visible bool + Blocks types.JSONText +} + +func NewPageEdit() *PageEdit { + return &PageEdit{} +} + +func (p *PageEdit) Fill(r *http.Request) error { + var err error + + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(p) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + r.ParseForm() + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + p.ID = parseUInt64(chi.URLParam(r, "id")) + if val, ok := post["selfID"]; ok { + + p.SelfID = parseUInt64(val) + } + if val, ok := post["moduleID"]; ok { + + p.ModuleID = parseUInt64(val) + } + if val, ok := post["title"]; ok { + + p.Title = val + } + if val, ok := post["description"]; ok { + + p.Description = val + } + if val, ok := post["visible"]; ok { + + p.Visible = parseBool(val) + } + if val, ok := post["blocks"]; ok { + + if p.Blocks, err = parseJSONText(val); err != nil { + return err + } + } + + return err +} + +var _ RequestFiller = NewPageEdit() + +// Page delete request parameters +type PageDelete struct { + ID uint64 +} + +func NewPageDelete() *PageDelete { + return &PageDelete{} +} + +func (p *PageDelete) Fill(r *http.Request) error { + var err error + + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(p) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + r.ParseForm() + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + p.ID = parseUInt64(chi.URLParam(r, "id")) + + return err +} + +var _ RequestFiller = NewPageDelete() diff --git a/crm/rest/router.go b/crm/rest/router.go index df1c65239..b308ffce1 100644 --- a/crm/rest/router.go +++ b/crm/rest/router.go @@ -1,10 +1,11 @@ package rest import ( + "github.com/go-chi/chi" + "github.com/crusttech/crust/crm/rest/handlers" "github.com/crusttech/crust/crm/service" "github.com/crusttech/crust/internal/auth" - "github.com/go-chi/chi" ) func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) { @@ -12,11 +13,13 @@ func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) { fieldSvc = service.Field() moduleSvc = service.Module() contentSvc = service.Content() + pageSvc = service.Page() ) var ( field = Field{}.New(fieldSvc) module = Module{}.New(moduleSvc, contentSvc) + page = Page{}.New(pageSvc) ) // Initialize handers & controllers. @@ -26,6 +29,7 @@ func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) { r.Use(auth.MiddlewareValidOnly) handlers.NewField(field).MountRoutes(r) + handlers.NewPage(page).MountRoutes(r) handlers.NewModule(module).MountRoutes(r) }) } diff --git a/crm/service/page.go b/crm/service/page.go new file mode 100644 index 000000000..3a346aacb --- /dev/null +++ b/crm/service/page.go @@ -0,0 +1,62 @@ +package service + +import ( + "context" + + "github.com/titpetric/factory" + + "github.com/crusttech/crust/crm/repository" + "github.com/crusttech/crust/crm/types" +) + +type ( + page struct { + db *factory.DB + ctx context.Context + repository repository.PageRepository + } + + PageService interface { + With(ctx context.Context) PageService + + FindByID(pageID uint64) (*types.Page, error) + Find() ([]*types.Page, error) + + Create(page *types.Page) (*types.Page, error) + Update(page *types.Page) (*types.Page, error) + DeleteByID(pageID uint64) error + } +) + +func Page() PageService { + return (&page{}).With(context.Background()) +} + +func (s *page) With(ctx context.Context) PageService { + db := repository.DB(ctx) + return &page{ + db: db, + ctx: ctx, + repository: repository.Page(ctx, db), + } +} + +func (s *page) FindByID(id uint64) (*types.Page, error) { + return s.repository.FindByID(id) +} + +func (s *page) Find() ([]*types.Page, error) { + return s.repository.Find() +} + +func (s *page) Create(mod *types.Page) (*types.Page, error) { + return s.repository.Create(mod) +} + +func (s *page) Update(mod *types.Page) (*types.Page, error) { + return s.repository.Update(mod) +} + +func (s *page) DeleteByID(id uint64) error { + return s.repository.DeleteByID(id) +} diff --git a/crm/types/types.go b/crm/types/types.go index 9fcc8d1ac..7ec11442b 100644 --- a/crm/types/types.go +++ b/crm/types/types.go @@ -57,4 +57,31 @@ type ( MaxLength int `json:"maxLength" db:"max_length"` Private bool `json:"isPrivate" db:"is_private"` } + + // Page - page structure + Page struct { + ID uint64 `json:"id" db:"id"` + SelfID uint64 `json:"selfID" db:"self_id"` + ModuleID uint64 `json:"moduleID" db:"module_id"` + + Title string `json:"title" db:"title"` + Description string `json:"description" db:"description"` + + Blocks types.JSONText `json:"blocks" db:"blocks"` + + Visible bool `json:"visible" db:"visible"` + Weight int `json:"-" db:"weight"` + } + + // Block - value of Page.Blocks ([]Block) + Block struct { + Title string `json:"title"` + Description string `json:"description"` + Options types.JSONText `json:"options"` + Kind string `json:"kind"` + X int `json:"x"` + Y int `json:"y"` + Width int `json:"width"` + Height int `json:"height"` + } )