Merge branch 'content-to-record'
This commit is contained in:
+24
-24
@@ -658,7 +658,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "content/report",
|
||||
"name": "record/report",
|
||||
"method": "GET",
|
||||
"title": "Generates report from module records",
|
||||
"path": "/{moduleID}/report",
|
||||
@@ -688,10 +688,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "content/list",
|
||||
"name": "record/list",
|
||||
"method": "GET",
|
||||
"title": "List/read contents from module section",
|
||||
"path": "/{moduleID}/content",
|
||||
"title": "List/read records from module section",
|
||||
"path": "/{moduleID}/record",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -730,10 +730,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "content/create",
|
||||
"name": "record/create",
|
||||
"method": "POST",
|
||||
"title": "List/read contents from module section",
|
||||
"path": "/{moduleID}/content",
|
||||
"title": "Create record in module section",
|
||||
"path": "/{moduleID}/record",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -748,16 +748,16 @@
|
||||
"type": "sqlxTypes.JSONText",
|
||||
"name": "fields",
|
||||
"required": true,
|
||||
"title": "Content JSON"
|
||||
"title": "Record JSON"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "content/read",
|
||||
"name": "record/read",
|
||||
"method": "GET",
|
||||
"title": "Read contents by ID from module section",
|
||||
"path": "/{moduleID}/content/{contentID}",
|
||||
"title": "Read records by ID from module section",
|
||||
"path": "/{moduleID}/record/{recordID}",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -768,18 +768,18 @@
|
||||
},
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "contentID",
|
||||
"name": "recordID",
|
||||
"required": true,
|
||||
"title": "Content ID"
|
||||
"title": "Record ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "content/edit",
|
||||
"name": "record/edit",
|
||||
"method": "POST",
|
||||
"title": "Add/update contents in module section",
|
||||
"path": "/{moduleID}/content/{contentID}",
|
||||
"title": "Add/update records in module section",
|
||||
"path": "/{moduleID}/record/{recordID}",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -790,9 +790,9 @@
|
||||
},
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "contentID",
|
||||
"name": "recordID",
|
||||
"required": true,
|
||||
"title": "Content ID"
|
||||
"title": "Record ID"
|
||||
}
|
||||
],
|
||||
"post": [
|
||||
@@ -800,16 +800,16 @@
|
||||
"type": "sqlxTypes.JSONText",
|
||||
"name": "fields",
|
||||
"required": true,
|
||||
"title": "Content JSON"
|
||||
"title": "Record JSON"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "content/delete",
|
||||
"name": "record/delete",
|
||||
"method": "DELETE",
|
||||
"title": "Delete content row from module section",
|
||||
"path": "/{moduleID}/content/{contentID}",
|
||||
"title": "Delete record row from module section",
|
||||
"path": "/{moduleID}/record/{recordID}",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -820,9 +820,9 @@
|
||||
},
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "contentID",
|
||||
"name": "recordID",
|
||||
"required": true,
|
||||
"title": "Content ID"
|
||||
"title": "Record ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+24
-24
@@ -117,7 +117,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "content/report",
|
||||
"Name": "record/report",
|
||||
"Method": "GET",
|
||||
"Title": "Generates report from module records",
|
||||
"Path": "/{moduleID}/report",
|
||||
@@ -147,10 +147,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "content/list",
|
||||
"Name": "record/list",
|
||||
"Method": "GET",
|
||||
"Title": "List/read contents from module section",
|
||||
"Path": "/{moduleID}/content",
|
||||
"Title": "List/read records from module section",
|
||||
"Path": "/{moduleID}/record",
|
||||
"Parameters": {
|
||||
"get": [
|
||||
{
|
||||
@@ -189,10 +189,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "content/create",
|
||||
"Name": "record/create",
|
||||
"Method": "POST",
|
||||
"Title": "List/read contents from module section",
|
||||
"Path": "/{moduleID}/content",
|
||||
"Title": "Create record in module section",
|
||||
"Path": "/{moduleID}/record",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -206,17 +206,17 @@
|
||||
{
|
||||
"name": "fields",
|
||||
"required": true,
|
||||
"title": "Content JSON",
|
||||
"title": "Record JSON",
|
||||
"type": "sqlxTypes.JSONText"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "content/read",
|
||||
"Name": "record/read",
|
||||
"Method": "GET",
|
||||
"Title": "Read contents by ID from module section",
|
||||
"Path": "/{moduleID}/content/{contentID}",
|
||||
"Title": "Read records by ID from module section",
|
||||
"Path": "/{moduleID}/record/{recordID}",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -226,19 +226,19 @@
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "contentID",
|
||||
"name": "recordID",
|
||||
"required": true,
|
||||
"title": "Content ID",
|
||||
"title": "Record ID",
|
||||
"type": "uint64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "content/edit",
|
||||
"Name": "record/edit",
|
||||
"Method": "POST",
|
||||
"Title": "Add/update contents in module section",
|
||||
"Path": "/{moduleID}/content/{contentID}",
|
||||
"Title": "Add/update records in module section",
|
||||
"Path": "/{moduleID}/record/{recordID}",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -248,9 +248,9 @@
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "contentID",
|
||||
"name": "recordID",
|
||||
"required": true,
|
||||
"title": "Content ID",
|
||||
"title": "Record ID",
|
||||
"type": "uint64"
|
||||
}
|
||||
],
|
||||
@@ -258,17 +258,17 @@
|
||||
{
|
||||
"name": "fields",
|
||||
"required": true,
|
||||
"title": "Content JSON",
|
||||
"title": "Record JSON",
|
||||
"type": "sqlxTypes.JSONText"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "content/delete",
|
||||
"Name": "record/delete",
|
||||
"Method": "DELETE",
|
||||
"Title": "Delete content row from module section",
|
||||
"Path": "/{moduleID}/content/{contentID}",
|
||||
"Title": "Delete record row from module section",
|
||||
"Path": "/{moduleID}/record/{recordID}",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
@@ -278,9 +278,9 @@
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "contentID",
|
||||
"name": "recordID",
|
||||
"required": true,
|
||||
"title": "Content ID",
|
||||
"title": "Record ID",
|
||||
"type": "uint64"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE `crm_content` RENAME TO `crm_record`;
|
||||
ALTER TABLE `crm_record` MODIFY COLUMN `json` json DEFAULT NULL COMMENT 'Records in JSON format.';
|
||||
|
||||
ALTER TABLE `crm_content_column` RENAME TO `crm_record_column`;
|
||||
ALTER TABLE `crm_record_column` CHANGE COLUMN `content_id` `record_id` bigint(20);
|
||||
|
||||
ALTER TABLE `crm_content_links` RENAME TO `crm_record_links`;
|
||||
ALTER TABLE `crm_record_links` CHANGE COLUMN `content_id` `record_id` bigint(20) unsigned;
|
||||
ALTER TABLE `crm_record_links` CHANGE COLUMN `rel_content_id` `rel_record_id` bigint(20) unsigned;
|
||||
@@ -98,7 +98,7 @@ func (r *module) Fields(mod *types.Module) ([]*types.ModuleField, error) {
|
||||
return fields, r.db().Select(&fields, "select * from crm_module_form where module_id=? order by place asc", mod.ID)
|
||||
}
|
||||
|
||||
// FieldNames returns a slice of field names, used for ordering content row columns
|
||||
// FieldNames returns a slice of field names, used for ordering record row columns
|
||||
func (r *module) FieldNames(mod *types.Module) ([]string, error) {
|
||||
if fields, err := r.Fields(mod); err != nil {
|
||||
return []string{}, err
|
||||
|
||||
@@ -14,19 +14,19 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
ContentRepository interface {
|
||||
With(ctx context.Context, db *factory.DB) ContentRepository
|
||||
RecordRepository interface {
|
||||
With(ctx context.Context, db *factory.DB) RecordRepository
|
||||
|
||||
FindByID(id uint64) (*types.Content, error)
|
||||
FindByID(id uint64) (*types.Record, error)
|
||||
|
||||
Report(moduleID uint64, params *types.ContentReport) (results interface{}, err error)
|
||||
Report(moduleID uint64, params *types.RecordReport) (results interface{}, err error)
|
||||
Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error)
|
||||
|
||||
Create(mod *types.Content) (*types.Content, error)
|
||||
Update(mod *types.Content) (*types.Content, error)
|
||||
Create(mod *types.Record) (*types.Record, error)
|
||||
Update(mod *types.Record) (*types.Record, error)
|
||||
DeleteByID(id uint64) error
|
||||
|
||||
Fields(mod *types.Content) ([]*types.ContentColumn, error)
|
||||
Fields(mod *types.Record) ([]*types.RecordColumn, error)
|
||||
}
|
||||
|
||||
FindResponseMeta struct {
|
||||
@@ -38,37 +38,37 @@ type (
|
||||
}
|
||||
|
||||
FindResponse struct {
|
||||
Meta FindResponseMeta `json:"meta"`
|
||||
Contents []*types.Content `json:"contents"`
|
||||
Meta FindResponseMeta `json:"meta"`
|
||||
Records []*types.Record `json:"records"`
|
||||
}
|
||||
|
||||
content struct {
|
||||
record struct {
|
||||
*repository
|
||||
}
|
||||
)
|
||||
|
||||
func Content(ctx context.Context, db *factory.DB) ContentRepository {
|
||||
return (&content{}).With(ctx, db)
|
||||
func Record(ctx context.Context, db *factory.DB) RecordRepository {
|
||||
return (&record{}).With(ctx, db)
|
||||
}
|
||||
|
||||
func (r *content) With(ctx context.Context, db *factory.DB) ContentRepository {
|
||||
return &content{
|
||||
func (r *record) With(ctx context.Context, db *factory.DB) RecordRepository {
|
||||
return &record{
|
||||
repository: r.repository.With(ctx, db),
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: update to accepted DeletedAt column semantics from SAM
|
||||
|
||||
func (r *content) FindByID(id uint64) (*types.Content, error) {
|
||||
mod := &types.Content{}
|
||||
if err := r.db().Get(mod, "SELECT * FROM crm_content WHERE id=? and deleted_at IS NULL", id); err != nil {
|
||||
func (r *record) FindByID(id uint64) (*types.Record, error) {
|
||||
mod := &types.Record{}
|
||||
if err := r.db().Get(mod, "SELECT * FROM crm_record WHERE id=? and deleted_at IS NULL", id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func (r *content) Report(moduleID uint64, params *types.ContentReport) (results interface{}, err error) {
|
||||
crb := NewContentReportBuilder(moduleID, params)
|
||||
func (r *record) Report(moduleID uint64, params *types.RecordReport) (results interface{}, err error) {
|
||||
crb := NewRecordReportBuilder(moduleID, params)
|
||||
|
||||
var result = make([]map[string]interface{}, 0)
|
||||
|
||||
@@ -85,7 +85,7 @@ func (r *content) Report(moduleID uint64, params *types.ContentReport) (results
|
||||
}
|
||||
}
|
||||
|
||||
func (r *content) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) {
|
||||
func (r *record) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) {
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
@@ -105,13 +105,13 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int, sor
|
||||
Query: query,
|
||||
Sort: sort,
|
||||
},
|
||||
Contents: make([]*types.Content, 0),
|
||||
Records: make([]*types.Record, 0),
|
||||
}
|
||||
|
||||
query = "%" + query + "%"
|
||||
|
||||
sqlSelect := "SELECT * FROM crm_content"
|
||||
sqlCount := "SELECT count(*) FROM crm_content"
|
||||
sqlSelect := "SELECT * FROM crm_record"
|
||||
sqlCount := "SELECT count(*) FROM crm_record"
|
||||
sqlWhere := "WHERE module_id=? and deleted_at IS NULL"
|
||||
sqlOrder := "ORDER BY id DESC"
|
||||
sqlLimit := fmt.Sprintf("LIMIT %d, %d", page*perPage, perPage)
|
||||
@@ -183,31 +183,31 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int, sor
|
||||
}
|
||||
|
||||
// One possibility to order by field value without JSON, is query written bellow with FIELD over column names and order by value:
|
||||
// SELECT * FROM crm_content
|
||||
// LEFT JOIN crm_content_column ON crm_content.id = crm_content_column.content_id"
|
||||
// SELECT * FROM crm_record
|
||||
// LEFT JOIN crm_record ON crm_record.id = crm_record_column.record_id"
|
||||
// WHERE column_name in ('name', 'email')
|
||||
// ORDER BY FIELD(column_name, 'email', 'name'), column_value;
|
||||
|
||||
// Possibility to order with JSON:
|
||||
// SELECT *,
|
||||
// JSON_UNQUOTE(JSON_EXTRACT(json, REPLACE(JSON_UNQUOTE(JSON_SEARCH(json, 'all', 'email')), '.name', '.value'))) as emailField
|
||||
// FROM crm_content
|
||||
// FROM crm_record
|
||||
// ORDER by emailField asc;
|
||||
|
||||
switch true {
|
||||
case query != "":
|
||||
sqlWhere = sqlWhere + " AND id in (select distinct content_id from crm_content_column where column_value like ?)"
|
||||
sqlWhere = sqlWhere + " AND id in (select distinct record_id from crm_record_column where column_value like ?)"
|
||||
if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.db().Select(&response.Contents, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID, query); err != nil {
|
||||
if err := r.db().Select(&response.Records, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.db().Select(&response.Contents, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID); err != nil {
|
||||
if err := r.db().Select(&response.Records, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -215,80 +215,80 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int, sor
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (r *content) Create(mod *types.Content) (*types.Content, error) {
|
||||
func (r *record) Create(mod *types.Record) (*types.Record, error) {
|
||||
mod.ID = factory.Sonyflake.NextID()
|
||||
mod.CreatedAt = time.Now()
|
||||
mod.UserID = Identity(r.Context())
|
||||
|
||||
fields := make([]types.ContentColumn, 0)
|
||||
fields := make([]types.RecordColumn, 0)
|
||||
if err := json.Unmarshal(mod.Fields, &fields); err != nil {
|
||||
return nil, errors.Wrap(err, "No content")
|
||||
}
|
||||
|
||||
r.db().Exec("delete from crm_content_links where content_id=?", mod.ID)
|
||||
r.db().Exec("delete from crm_record_links where record_id=?", mod.ID)
|
||||
for _, v := range fields {
|
||||
v.ContentID = mod.ID
|
||||
if err := r.db().Replace("crm_content_column", v); err != nil {
|
||||
v.RecordID = mod.ID
|
||||
if err := r.db().Replace("crm_record_column", v); err != nil {
|
||||
return nil, errors.Wrap(err, "Error adding columns")
|
||||
}
|
||||
for _, related := range v.Related {
|
||||
row := types.Related{
|
||||
ContentID: v.ContentID,
|
||||
Name: v.Name,
|
||||
RelatedContentID: related,
|
||||
RecordID: v.RecordID,
|
||||
Name: v.Name,
|
||||
RelatedRecordID: related,
|
||||
}
|
||||
if err := r.db().Replace("crm_content_links", row); err != nil {
|
||||
if err := r.db().Replace("crm_record_links", row); err != nil {
|
||||
return nil, errors.Wrap(err, "Error adding column links")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.db().Insert("crm_content", mod); err != nil {
|
||||
if err := r.db().Insert("crm_record", mod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func (r *content) Update(mod *types.Content) (*types.Content, error) {
|
||||
func (r *record) Update(mod *types.Record) (*types.Record, error) {
|
||||
now := time.Now()
|
||||
mod.UpdatedAt = &now
|
||||
|
||||
fields := make([]types.ContentColumn, 0)
|
||||
fields := make([]types.RecordColumn, 0)
|
||||
if err := json.Unmarshal(mod.Fields, &fields); err != nil {
|
||||
return nil, errors.Wrap(err, "Error when saving content, no content")
|
||||
return nil, errors.Wrap(err, "Error when saving record, no content")
|
||||
}
|
||||
|
||||
r.db().Exec("delete from crm_content_links where content_id=?", mod.ID)
|
||||
r.db().Exec("delete from crm_record_links where record_id=?", mod.ID)
|
||||
for _, v := range fields {
|
||||
v.ContentID = mod.ID
|
||||
if err := r.db().Replace("crm_content_column", v); err != nil {
|
||||
v.RecordID = mod.ID
|
||||
if err := r.db().Replace("crm_record_column", v); err != nil {
|
||||
return nil, errors.Wrap(err, "Error adding columns to database")
|
||||
}
|
||||
for _, related := range v.Related {
|
||||
row := types.Related{
|
||||
ContentID: v.ContentID,
|
||||
Name: v.Name,
|
||||
RelatedContentID: related,
|
||||
RecordID: v.RecordID,
|
||||
Name: v.Name,
|
||||
RelatedRecordID: related,
|
||||
}
|
||||
if err := r.db().Replace("crm_content_links", row); err != nil {
|
||||
if err := r.db().Replace("crm_record_links", row); err != nil {
|
||||
return nil, errors.Wrap(err, "Error adding column links")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod, r.db().Replace("crm_content", mod)
|
||||
return mod, r.db().Replace("crm_record", mod)
|
||||
}
|
||||
|
||||
func (r *content) DeleteByID(id uint64) error {
|
||||
_, err := r.db().Exec("update crm_content set deleted_at=? where id=?", time.Now(), id)
|
||||
func (r *record) DeleteByID(id uint64) error {
|
||||
_, err := r.db().Exec("update crm_record set deleted_at=? where id=?", time.Now(), id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *content) Fields(content *types.Content) ([]*types.ContentColumn, error) {
|
||||
result := make([]*types.ContentColumn, 0)
|
||||
func (r *record) Fields(record *types.Record) ([]*types.RecordColumn, error) {
|
||||
result := make([]*types.RecordColumn, 0)
|
||||
module := Module(r.ctx, r.db())
|
||||
|
||||
mod, err := module.FindByID(content.ModuleID)
|
||||
mod, err := module.FindByID(record.ModuleID)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@@ -303,10 +303,10 @@ func (r *content) Fields(content *types.Content) ([]*types.ContentColumn, error)
|
||||
|
||||
order := "FIELD(column_name" + strings.Repeat(",?", len(fieldNames)) + ")"
|
||||
args := []interface{}{
|
||||
content.ID,
|
||||
record.ID,
|
||||
}
|
||||
for _, v := range fieldNames {
|
||||
args = append(args, v)
|
||||
}
|
||||
return result, r.db().Select(&result, "select * FROM crm_content_column where content_id=? order by "+order, args...)
|
||||
return result, r.db().Select(&result, "select * FROM crm_record_column where record_id=? order by "+order, args...)
|
||||
}
|
||||
+14
-14
@@ -14,27 +14,27 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
contentReportBuilder struct {
|
||||
recordReportBuilder struct {
|
||||
jsonField string
|
||||
|
||||
moduleID uint64
|
||||
params *types.ContentReport
|
||||
params *types.RecordReport
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
contentReportExprMatch = regexp.MustCompile(`^\s*(\w+)\((.+)\)\s*$`)
|
||||
recordReportExprMatch = regexp.MustCompile(`^\s*(\w+)\((.+)\)\s*$`)
|
||||
)
|
||||
|
||||
func NewContentReportBuilder(moduleID uint64, params *types.ContentReport) *contentReportBuilder {
|
||||
return &contentReportBuilder{
|
||||
func NewRecordReportBuilder(moduleID uint64, params *types.RecordReport) *recordReportBuilder {
|
||||
return &recordReportBuilder{
|
||||
moduleID: moduleID,
|
||||
params: params,
|
||||
jsonField: `JSON_UNQUOTE(JSON_EXTRACT(json, REPLACE(JSON_UNQUOTE(JSON_SEARCH(json, 'one', ?)), '.name', '.value')))`,
|
||||
}
|
||||
}
|
||||
|
||||
func (b contentReportBuilder) field(name string) squirrel.Sqlizer {
|
||||
func (b recordReportBuilder) field(name string) squirrel.Sqlizer {
|
||||
switch name {
|
||||
case "created_at", "updated_at":
|
||||
return squirrel.Expr(name)
|
||||
@@ -43,7 +43,7 @@ func (b contentReportBuilder) field(name string) squirrel.Sqlizer {
|
||||
}
|
||||
}
|
||||
|
||||
func (b contentReportBuilder) alias(col squirrel.Sqlizer, alias, fallback string) (squirrel.Sqlizer, string) {
|
||||
func (b recordReportBuilder) alias(col squirrel.Sqlizer, alias, fallback string) (squirrel.Sqlizer, string) {
|
||||
if alias != "" {
|
||||
return squirrel.Alias(col, alias), alias
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func (b contentReportBuilder) alias(col squirrel.Sqlizer, alias, fallback string
|
||||
return squirrel.Alias(col, fallback), fallback
|
||||
}
|
||||
|
||||
func (b contentReportBuilder) wrapInModifiers(col squirrel.Sqlizer, mm ...string) squirrel.Sqlizer {
|
||||
func (b recordReportBuilder) wrapInModifiers(col squirrel.Sqlizer, mm ...string) squirrel.Sqlizer {
|
||||
for _, m := range mm {
|
||||
switch strings.ToUpper(m) {
|
||||
case "WEEKDAY":
|
||||
@@ -72,8 +72,8 @@ func (b contentReportBuilder) wrapInModifiers(col squirrel.Sqlizer, mm ...string
|
||||
return col
|
||||
}
|
||||
|
||||
func (b contentReportBuilder) parseExpression(exp string) squirrel.Sqlizer {
|
||||
res := contentReportExprMatch.FindStringSubmatch(exp)
|
||||
func (b recordReportBuilder) parseExpression(exp string) squirrel.Sqlizer {
|
||||
res := recordReportExprMatch.FindStringSubmatch(exp)
|
||||
if len(res) > 0 {
|
||||
aggrFuncName := strings.ToUpper(res[1])
|
||||
aggrFuncArgs := b.parseExpression(res[2])
|
||||
@@ -92,15 +92,15 @@ func (b contentReportBuilder) parseExpression(exp string) squirrel.Sqlizer {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *contentReportBuilder) Build() (sql string, args []interface{}, err error) {
|
||||
func (b *recordReportBuilder) Build() (sql string, args []interface{}, err error) {
|
||||
report := squirrel.
|
||||
Select().
|
||||
Column(squirrel.Alias(squirrel.Expr("COUNT(*)"), "count")).
|
||||
From("crm_content").
|
||||
From("crm_record").
|
||||
Where("module_id = ?", b.moduleID)
|
||||
|
||||
if b.params == nil {
|
||||
return "", nil, errors.New("Can not generate report without parameters")
|
||||
return "", nil, errors.New("can not generate report without parameters")
|
||||
}
|
||||
|
||||
for i, m := range b.params.Metrics {
|
||||
@@ -129,7 +129,7 @@ func (b *contentReportBuilder) Build() (sql string, args []interface{}, err erro
|
||||
return report.ToSql()
|
||||
}
|
||||
|
||||
func (b contentReportBuilder) Cast(row sqlx.ColScanner) map[string]interface{} {
|
||||
func (b recordReportBuilder) Cast(row sqlx.ColScanner) map[string]interface{} {
|
||||
out := map[string]interface{}{}
|
||||
sqlx.MapScan(row, out)
|
||||
for k, v := range out {
|
||||
+2
-2
@@ -4,8 +4,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContentReportBuilder_parseExpression(t *testing.T) {
|
||||
b := contentReportBuilder{jsonField: "JSONFIELD"}
|
||||
func TestRecordReportBuilder_parseExpression(t *testing.T) {
|
||||
b := recordReportBuilder{jsonField: "JSONFIELD"}
|
||||
|
||||
tc := []struct {
|
||||
exp string
|
||||
+41
-41
@@ -32,27 +32,27 @@ type ModuleAPI interface {
|
||||
Read(context.Context, *request.ModuleRead) (interface{}, error)
|
||||
Edit(context.Context, *request.ModuleEdit) (interface{}, error)
|
||||
Delete(context.Context, *request.ModuleDelete) (interface{}, error)
|
||||
ContentReport(context.Context, *request.ModuleContentReport) (interface{}, error)
|
||||
ContentList(context.Context, *request.ModuleContentList) (interface{}, error)
|
||||
ContentCreate(context.Context, *request.ModuleContentCreate) (interface{}, error)
|
||||
ContentRead(context.Context, *request.ModuleContentRead) (interface{}, error)
|
||||
ContentEdit(context.Context, *request.ModuleContentEdit) (interface{}, error)
|
||||
ContentDelete(context.Context, *request.ModuleContentDelete) (interface{}, error)
|
||||
RecordReport(context.Context, *request.ModuleRecordReport) (interface{}, error)
|
||||
RecordList(context.Context, *request.ModuleRecordList) (interface{}, error)
|
||||
RecordCreate(context.Context, *request.ModuleRecordCreate) (interface{}, error)
|
||||
RecordRead(context.Context, *request.ModuleRecordRead) (interface{}, error)
|
||||
RecordEdit(context.Context, *request.ModuleRecordEdit) (interface{}, error)
|
||||
RecordDelete(context.Context, *request.ModuleRecordDelete) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
type Module 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)
|
||||
ContentReport func(http.ResponseWriter, *http.Request)
|
||||
ContentList func(http.ResponseWriter, *http.Request)
|
||||
ContentCreate func(http.ResponseWriter, *http.Request)
|
||||
ContentRead func(http.ResponseWriter, *http.Request)
|
||||
ContentEdit func(http.ResponseWriter, *http.Request)
|
||||
ContentDelete func(http.ResponseWriter, *http.Request)
|
||||
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)
|
||||
RecordReport func(http.ResponseWriter, *http.Request)
|
||||
RecordList func(http.ResponseWriter, *http.Request)
|
||||
RecordCreate func(http.ResponseWriter, *http.Request)
|
||||
RecordRead func(http.ResponseWriter, *http.Request)
|
||||
RecordEdit func(http.ResponseWriter, *http.Request)
|
||||
RecordDelete func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
func NewModule(mh ModuleAPI) *Module {
|
||||
@@ -92,46 +92,46 @@ func NewModule(mh ModuleAPI) *Module {
|
||||
return mh.Delete(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ContentReport: func(w http.ResponseWriter, r *http.Request) {
|
||||
RecordReport: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewModuleContentReport()
|
||||
params := request.NewModuleRecordReport()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ContentReport(r.Context(), params)
|
||||
return mh.RecordReport(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ContentList: func(w http.ResponseWriter, r *http.Request) {
|
||||
RecordList: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewModuleContentList()
|
||||
params := request.NewModuleRecordList()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ContentList(r.Context(), params)
|
||||
return mh.RecordList(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ContentCreate: func(w http.ResponseWriter, r *http.Request) {
|
||||
RecordCreate: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewModuleContentCreate()
|
||||
params := request.NewModuleRecordCreate()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ContentCreate(r.Context(), params)
|
||||
return mh.RecordCreate(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ContentRead: func(w http.ResponseWriter, r *http.Request) {
|
||||
RecordRead: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewModuleContentRead()
|
||||
params := request.NewModuleRecordRead()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ContentRead(r.Context(), params)
|
||||
return mh.RecordRead(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ContentEdit: func(w http.ResponseWriter, r *http.Request) {
|
||||
RecordEdit: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewModuleContentEdit()
|
||||
params := request.NewModuleRecordEdit()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ContentEdit(r.Context(), params)
|
||||
return mh.RecordEdit(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ContentDelete: func(w http.ResponseWriter, r *http.Request) {
|
||||
RecordDelete: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewModuleContentDelete()
|
||||
params := request.NewModuleRecordDelete()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ContentDelete(r.Context(), params)
|
||||
return mh.RecordDelete(r.Context(), params)
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -146,12 +146,12 @@ func (mh *Module) MountRoutes(r chi.Router, middlewares ...func(http.Handler) ht
|
||||
r.Get("/{moduleID}", mh.Read)
|
||||
r.Post("/{moduleID}", mh.Edit)
|
||||
r.Delete("/{moduleID}", mh.Delete)
|
||||
r.Get("/{moduleID}/report", mh.ContentReport)
|
||||
r.Get("/{moduleID}/content", mh.ContentList)
|
||||
r.Post("/{moduleID}/content", mh.ContentCreate)
|
||||
r.Get("/{moduleID}/content/{contentID}", mh.ContentRead)
|
||||
r.Post("/{moduleID}/content/{contentID}", mh.ContentEdit)
|
||||
r.Delete("/{moduleID}/content/{contentID}", mh.ContentDelete)
|
||||
r.Get("/{moduleID}/report", mh.RecordReport)
|
||||
r.Get("/{moduleID}/record", mh.RecordList)
|
||||
r.Post("/{moduleID}/record", mh.RecordCreate)
|
||||
r.Get("/{moduleID}/record/{recordID}", mh.RecordRead)
|
||||
r.Post("/{moduleID}/record/{recordID}", mh.RecordEdit)
|
||||
r.Delete("/{moduleID}/record/{recordID}", mh.RecordDelete)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
+14
-14
@@ -14,14 +14,14 @@ import (
|
||||
type (
|
||||
Module struct {
|
||||
module service.ModuleService
|
||||
content service.ContentService
|
||||
content service.RecordService
|
||||
}
|
||||
)
|
||||
|
||||
func (Module) New() *Module {
|
||||
return &Module{
|
||||
module: service.DefaultModule,
|
||||
content: service.DefaultContent,
|
||||
content: service.DefaultRecord,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ func (s *Module) Edit(ctx context.Context, r *request.ModuleEdit) (interface{},
|
||||
return s.module.With(ctx).Update(item)
|
||||
}
|
||||
|
||||
func (s *Module) ContentReport(ctx context.Context, r *request.ModuleContentReport) (interface{}, error) {
|
||||
reportParams := &types.ContentReport{}
|
||||
func (s *Module) RecordReport(ctx context.Context, r *request.ModuleRecordReport) (interface{}, error) {
|
||||
reportParams := &types.RecordReport{}
|
||||
|
||||
if strings.TrimSpace(r.Metrics) != "" {
|
||||
reportParams.ScanMetrics(strings.Split(r.Metrics, ",")...)
|
||||
@@ -68,31 +68,31 @@ func (s *Module) ContentReport(ctx context.Context, r *request.ModuleContentRepo
|
||||
return s.content.With(ctx).Report(r.ModuleID, reportParams)
|
||||
}
|
||||
|
||||
func (s *Module) ContentList(ctx context.Context, r *request.ModuleContentList) (interface{}, error) {
|
||||
func (s *Module) RecordList(ctx context.Context, r *request.ModuleRecordList) (interface{}, error) {
|
||||
return s.content.With(ctx).Find(r.ModuleID, r.Query, r.Page, r.PerPage, r.Sort)
|
||||
}
|
||||
|
||||
func (s *Module) ContentRead(ctx context.Context, r *request.ModuleContentRead) (interface{}, error) {
|
||||
return s.content.With(ctx).FindByID(r.ContentID)
|
||||
func (s *Module) RecordRead(ctx context.Context, r *request.ModuleRecordRead) (interface{}, error) {
|
||||
return s.content.With(ctx).FindByID(r.RecordID)
|
||||
}
|
||||
|
||||
func (s *Module) ContentCreate(ctx context.Context, r *request.ModuleContentCreate) (interface{}, error) {
|
||||
item := &types.Content{
|
||||
func (s *Module) RecordCreate(ctx context.Context, r *request.ModuleRecordCreate) (interface{}, error) {
|
||||
item := &types.Record{
|
||||
ModuleID: r.ModuleID,
|
||||
Fields: r.Fields,
|
||||
}
|
||||
return s.content.With(ctx).Create(item)
|
||||
}
|
||||
|
||||
func (s *Module) ContentEdit(ctx context.Context, r *request.ModuleContentEdit) (interface{}, error) {
|
||||
item := &types.Content{
|
||||
ID: r.ContentID,
|
||||
func (s *Module) RecordEdit(ctx context.Context, r *request.ModuleRecordEdit) (interface{}, error) {
|
||||
item := &types.Record{
|
||||
ID: r.RecordID,
|
||||
ModuleID: r.ModuleID,
|
||||
Fields: r.Fields,
|
||||
}
|
||||
return s.content.With(ctx).Update(item)
|
||||
}
|
||||
|
||||
func (s *Module) ContentDelete(ctx context.Context, r *request.ModuleContentDelete) (interface{}, error) {
|
||||
return resputil.OK(), s.content.With(ctx).DeleteByID(r.ContentID)
|
||||
func (s *Module) RecordDelete(ctx context.Context, r *request.ModuleRecordDelete) (interface{}, error) {
|
||||
return resputil.OK(), s.content.With(ctx).DeleteByID(r.RecordID)
|
||||
}
|
||||
|
||||
+46
-46
@@ -260,18 +260,18 @@ func (m *ModuleDelete) Fill(r *http.Request) (err error) {
|
||||
|
||||
var _ RequestFiller = NewModuleDelete()
|
||||
|
||||
// Module content/report request parameters
|
||||
type ModuleContentReport struct {
|
||||
// Module record/report request parameters
|
||||
type ModuleRecordReport struct {
|
||||
Metrics string
|
||||
Dimensions string
|
||||
ModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewModuleContentReport() *ModuleContentReport {
|
||||
return &ModuleContentReport{}
|
||||
func NewModuleRecordReport() *ModuleRecordReport {
|
||||
return &ModuleRecordReport{}
|
||||
}
|
||||
|
||||
func (m *ModuleContentReport) Fill(r *http.Request) (err error) {
|
||||
func (m *ModuleRecordReport) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(m)
|
||||
|
||||
@@ -311,10 +311,10 @@ func (m *ModuleContentReport) Fill(r *http.Request) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewModuleContentReport()
|
||||
var _ RequestFiller = NewModuleRecordReport()
|
||||
|
||||
// Module content/list request parameters
|
||||
type ModuleContentList struct {
|
||||
// Module record/list request parameters
|
||||
type ModuleRecordList struct {
|
||||
Query string
|
||||
Page int
|
||||
PerPage int
|
||||
@@ -322,11 +322,11 @@ type ModuleContentList struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewModuleContentList() *ModuleContentList {
|
||||
return &ModuleContentList{}
|
||||
func NewModuleRecordList() *ModuleRecordList {
|
||||
return &ModuleRecordList{}
|
||||
}
|
||||
|
||||
func (m *ModuleContentList) Fill(r *http.Request) (err error) {
|
||||
func (m *ModuleRecordList) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(m)
|
||||
|
||||
@@ -374,19 +374,19 @@ func (m *ModuleContentList) Fill(r *http.Request) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewModuleContentList()
|
||||
var _ RequestFiller = NewModuleRecordList()
|
||||
|
||||
// Module content/create request parameters
|
||||
type ModuleContentCreate struct {
|
||||
// Module record/create request parameters
|
||||
type ModuleRecordCreate struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
Fields sqlxTypes.JSONText
|
||||
}
|
||||
|
||||
func NewModuleContentCreate() *ModuleContentCreate {
|
||||
return &ModuleContentCreate{}
|
||||
func NewModuleRecordCreate() *ModuleRecordCreate {
|
||||
return &ModuleRecordCreate{}
|
||||
}
|
||||
|
||||
func (m *ModuleContentCreate) Fill(r *http.Request) (err error) {
|
||||
func (m *ModuleRecordCreate) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(m)
|
||||
|
||||
@@ -424,19 +424,19 @@ func (m *ModuleContentCreate) Fill(r *http.Request) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewModuleContentCreate()
|
||||
var _ RequestFiller = NewModuleRecordCreate()
|
||||
|
||||
// Module content/read request parameters
|
||||
type ModuleContentRead struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
ContentID uint64 `json:",string"`
|
||||
// Module record/read request parameters
|
||||
type ModuleRecordRead struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
RecordID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewModuleContentRead() *ModuleContentRead {
|
||||
return &ModuleContentRead{}
|
||||
func NewModuleRecordRead() *ModuleRecordRead {
|
||||
return &ModuleRecordRead{}
|
||||
}
|
||||
|
||||
func (m *ModuleContentRead) Fill(r *http.Request) (err error) {
|
||||
func (m *ModuleRecordRead) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(m)
|
||||
|
||||
@@ -464,25 +464,25 @@ func (m *ModuleContentRead) Fill(r *http.Request) (err error) {
|
||||
}
|
||||
|
||||
m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID"))
|
||||
m.ContentID = parseUInt64(chi.URLParam(r, "contentID"))
|
||||
m.RecordID = parseUInt64(chi.URLParam(r, "recordID"))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewModuleContentRead()
|
||||
var _ RequestFiller = NewModuleRecordRead()
|
||||
|
||||
// Module content/edit request parameters
|
||||
type ModuleContentEdit struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
ContentID uint64 `json:",string"`
|
||||
Fields sqlxTypes.JSONText
|
||||
// Module record/edit request parameters
|
||||
type ModuleRecordEdit struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
RecordID uint64 `json:",string"`
|
||||
Fields sqlxTypes.JSONText
|
||||
}
|
||||
|
||||
func NewModuleContentEdit() *ModuleContentEdit {
|
||||
return &ModuleContentEdit{}
|
||||
func NewModuleRecordEdit() *ModuleRecordEdit {
|
||||
return &ModuleRecordEdit{}
|
||||
}
|
||||
|
||||
func (m *ModuleContentEdit) Fill(r *http.Request) (err error) {
|
||||
func (m *ModuleRecordEdit) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(m)
|
||||
|
||||
@@ -510,7 +510,7 @@ func (m *ModuleContentEdit) Fill(r *http.Request) (err error) {
|
||||
}
|
||||
|
||||
m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID"))
|
||||
m.ContentID = parseUInt64(chi.URLParam(r, "contentID"))
|
||||
m.RecordID = parseUInt64(chi.URLParam(r, "recordID"))
|
||||
if val, ok := post["fields"]; ok {
|
||||
|
||||
if m.Fields, err = parseJSONTextWithErr(val); err != nil {
|
||||
@@ -521,19 +521,19 @@ func (m *ModuleContentEdit) Fill(r *http.Request) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewModuleContentEdit()
|
||||
var _ RequestFiller = NewModuleRecordEdit()
|
||||
|
||||
// Module content/delete request parameters
|
||||
type ModuleContentDelete struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
ContentID uint64 `json:",string"`
|
||||
// Module record/delete request parameters
|
||||
type ModuleRecordDelete struct {
|
||||
ModuleID uint64 `json:",string"`
|
||||
RecordID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewModuleContentDelete() *ModuleContentDelete {
|
||||
return &ModuleContentDelete{}
|
||||
func NewModuleRecordDelete() *ModuleRecordDelete {
|
||||
return &ModuleRecordDelete{}
|
||||
}
|
||||
|
||||
func (m *ModuleContentDelete) Fill(r *http.Request) (err error) {
|
||||
func (m *ModuleRecordDelete) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(m)
|
||||
|
||||
@@ -561,9 +561,9 @@ func (m *ModuleContentDelete) Fill(r *http.Request) (err error) {
|
||||
}
|
||||
|
||||
m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID"))
|
||||
m.ContentID = parseUInt64(chi.URLParam(r, "contentID"))
|
||||
m.RecordID = parseUInt64(chi.URLParam(r, "recordID"))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewModuleContentDelete()
|
||||
var _ RequestFiller = NewModuleRecordDelete()
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/crm/repository"
|
||||
"github.com/crusttech/crust/crm/types"
|
||||
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
)
|
||||
|
||||
type (
|
||||
content struct {
|
||||
db *factory.DB
|
||||
ctx context.Context
|
||||
|
||||
repository repository.ContentRepository
|
||||
pageRepo repository.PageRepository
|
||||
|
||||
userSvc systemService.UserService
|
||||
}
|
||||
|
||||
ContentService interface {
|
||||
With(ctx context.Context) ContentService
|
||||
|
||||
FindByID(contentID uint64) (*types.Content, error)
|
||||
|
||||
Report(moduleID uint64, params *types.ContentReport) (interface{}, error)
|
||||
Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error)
|
||||
|
||||
Create(content *types.Content) (*types.Content, error)
|
||||
Update(content *types.Content) (*types.Content, error)
|
||||
DeleteByID(contentID uint64) error
|
||||
|
||||
Fields(mod *types.Content) ([]*types.ContentColumn, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Content() ContentService {
|
||||
return (&content{
|
||||
userSvc: systemService.DefaultUser,
|
||||
}).With(context.Background())
|
||||
}
|
||||
|
||||
func (s *content) With(ctx context.Context) ContentService {
|
||||
db := repository.DB(ctx)
|
||||
return &content{
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
repository: repository.Content(ctx, db),
|
||||
pageRepo: repository.Page(ctx, db),
|
||||
userSvc: s.userSvc.With(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *content) FindByID(id uint64) (*types.Content, error) {
|
||||
response, err := s.repository.FindByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, s.preload(response, "page", "user", "fields")
|
||||
}
|
||||
|
||||
func (s *content) Report(moduleID uint64, params *types.ContentReport) (interface{}, error) {
|
||||
return s.repository.Report(moduleID, params)
|
||||
}
|
||||
|
||||
func (s *content) Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) {
|
||||
response, err := s.repository.Find(moduleID, query, page, perPage, sort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.preloadAll(response.Contents, "user", "fields"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *content) Create(mod *types.Content) (*types.Content, error) {
|
||||
response, err := s.repository.Create(mod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, s.preload(response, "user", "fields")
|
||||
}
|
||||
|
||||
func (s *content) Update(content *types.Content) (c *types.Content, err error) {
|
||||
validate := func() error {
|
||||
if content.ID == 0 {
|
||||
return errors.New("Error updating content: invalid ID")
|
||||
} else if c, err = s.repository.FindByID(content.ID); err != nil {
|
||||
return errors.Wrap(err, "Error while loading content for update")
|
||||
} else {
|
||||
content.CreatedAt = c.CreatedAt
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, s.db.Transaction(func() (err error) {
|
||||
c, err = s.repository.Update(content)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (s *content) Fields(mod *types.Content) ([]*types.ContentColumn, error) {
|
||||
return s.repository.Fields(mod)
|
||||
}
|
||||
|
||||
func (s *content) DeleteByID(id uint64) error {
|
||||
return s.repository.DeleteByID(id)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/crusttech/crust/crm/types"
|
||||
)
|
||||
|
||||
func (r *content) preloadAll(contents []*types.Content, fields ...string) error {
|
||||
for _, content := range contents {
|
||||
if err := r.preload(content, fields...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *content) preload(content *types.Content, fields ...string) (err error) {
|
||||
for _, field := range fields {
|
||||
switch field {
|
||||
case "fields":
|
||||
fields, err := r.Fields(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json, err := json.Marshal(fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (&content.Fields).Scan(json); err != nil {
|
||||
return err
|
||||
}
|
||||
case "page":
|
||||
if content.Page, err = r.pageRepo.FindByModuleID(content.ModuleID); err != nil {
|
||||
return
|
||||
}
|
||||
case "user":
|
||||
if content.UserID > 0 {
|
||||
if content.User, err = r.userSvc.FindByID(content.UserID); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
// clean up tables
|
||||
{
|
||||
for _, name := range []string{"crm_module", "crm_module_form", "crm_content", "crm_content_column", "crm_page", "sys_user"} {
|
||||
for _, name := range []string{"crm_module", "crm_module_form", "crm_record", "crm_record_column", "crm_page", "sys_user"} {
|
||||
_, err := db.Exec("truncate " + name)
|
||||
if err != nil {
|
||||
panic("Error when clearing " + name + ": " + err.Error())
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestModule(t *testing.T) {
|
||||
{
|
||||
{
|
||||
m, err := repository.Update(module)
|
||||
assert(t, m == nil, "Expected empty return for ivalid update, got %#v", m)
|
||||
assert(t, m == nil, "Expected empty return for invalid update, got %#v", m)
|
||||
assert(t, err != nil, "Expected error when updating invalid content")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/crm/repository"
|
||||
"github.com/crusttech/crust/crm/types"
|
||||
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
)
|
||||
|
||||
type (
|
||||
record struct {
|
||||
db *factory.DB
|
||||
ctx context.Context
|
||||
|
||||
repository repository.RecordRepository
|
||||
pageRepo repository.PageRepository
|
||||
|
||||
userSvc systemService.UserService
|
||||
}
|
||||
|
||||
RecordService interface {
|
||||
With(ctx context.Context) RecordService
|
||||
|
||||
FindByID(recordID uint64) (*types.Record, error)
|
||||
|
||||
Report(moduleID uint64, params *types.RecordReport) (interface{}, error)
|
||||
Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error)
|
||||
|
||||
Create(record *types.Record) (*types.Record, error)
|
||||
Update(record *types.Record) (*types.Record, error)
|
||||
DeleteByID(recordID uint64) error
|
||||
|
||||
Fields(mod *types.Record) ([]*types.RecordColumn, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Record() RecordService {
|
||||
return (&record{
|
||||
userSvc: systemService.DefaultUser,
|
||||
}).With(context.Background())
|
||||
}
|
||||
|
||||
func (s *record) With(ctx context.Context) RecordService {
|
||||
db := repository.DB(ctx)
|
||||
return &record{
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
repository: repository.Record(ctx, db),
|
||||
pageRepo: repository.Page(ctx, db),
|
||||
userSvc: s.userSvc.With(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *record) FindByID(id uint64) (*types.Record, error) {
|
||||
response, err := s.repository.FindByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, s.preload(response, "page", "user", "fields")
|
||||
}
|
||||
|
||||
func (s *record) Report(moduleID uint64, params *types.RecordReport) (interface{}, error) {
|
||||
return s.repository.Report(moduleID, params)
|
||||
}
|
||||
|
||||
func (s *record) Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) {
|
||||
response, err := s.repository.Find(moduleID, query, page, perPage, sort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.preloadAll(response.Records, "user", "fields"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *record) Create(mod *types.Record) (*types.Record, error) {
|
||||
response, err := s.repository.Create(mod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, s.preload(response, "user", "fields")
|
||||
}
|
||||
|
||||
func (s *record) Update(record *types.Record) (c *types.Record, err error) {
|
||||
validate := func() error {
|
||||
if record.ID == 0 {
|
||||
return errors.New("Error updating record: invalid ID")
|
||||
} else if c, err = s.repository.FindByID(record.ID); err != nil {
|
||||
return errors.Wrap(err, "Error while loading record for update")
|
||||
} else {
|
||||
record.CreatedAt = c.CreatedAt
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, s.db.Transaction(func() (err error) {
|
||||
c, err = s.repository.Update(record)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (s *record) Fields(mod *types.Record) ([]*types.RecordColumn, error) {
|
||||
return s.repository.Fields(mod)
|
||||
}
|
||||
|
||||
func (s *record) DeleteByID(id uint64) error {
|
||||
return s.repository.DeleteByID(id)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
systemTypes "github.com/crusttech/crust/system/types"
|
||||
)
|
||||
|
||||
func TestContent(t *testing.T) {
|
||||
func TestRecord(t *testing.T) {
|
||||
user := &systemTypes.User{
|
||||
ID: 1337,
|
||||
Username: "TestUser",
|
||||
@@ -31,7 +31,7 @@ func TestContent(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := auth.SetIdentityToContext(context.Background(), auth.NewIdentity(user.Identity()))
|
||||
repository := Content().With(ctx)
|
||||
repository := Record().With(ctx)
|
||||
|
||||
module := &types.Module{
|
||||
Name: "Test",
|
||||
@@ -62,92 +62,92 @@ func TestContent(t *testing.T) {
|
||||
assert(t, module.ID > 0, "Expected auto generated ID")
|
||||
}
|
||||
|
||||
columns := []types.ContentColumn{
|
||||
types.ContentColumn{
|
||||
columns := []types.RecordColumn{
|
||||
types.RecordColumn{
|
||||
Name: "name",
|
||||
Value: "Tit Petric",
|
||||
},
|
||||
types.ContentColumn{
|
||||
types.RecordColumn{
|
||||
Name: "email",
|
||||
Value: "tit.petric@example.com",
|
||||
},
|
||||
types.ContentColumn{
|
||||
types.RecordColumn{
|
||||
Name: "options",
|
||||
Related: []string{"1", "2", "3"},
|
||||
},
|
||||
types.ContentColumn{
|
||||
types.RecordColumn{
|
||||
Name: "description",
|
||||
Value: "jack of all trades",
|
||||
},
|
||||
}
|
||||
|
||||
content1 := &types.Content{
|
||||
record1 := &types.Record{
|
||||
ModuleID: module.ID,
|
||||
}
|
||||
(&content1.Fields).Scan(func() []byte {
|
||||
(&record1.Fields).Scan(func() []byte {
|
||||
b, _ := json.Marshal(columns)
|
||||
return b
|
||||
}())
|
||||
|
||||
columns2 := []types.ContentColumn{
|
||||
types.ContentColumn{
|
||||
columns2 := []types.RecordColumn{
|
||||
types.RecordColumn{
|
||||
Name: "name",
|
||||
Value: "Marko Novak",
|
||||
},
|
||||
types.ContentColumn{
|
||||
types.RecordColumn{
|
||||
Name: "email",
|
||||
Value: "marko.n@example.com",
|
||||
},
|
||||
types.ContentColumn{
|
||||
types.RecordColumn{
|
||||
Name: "options",
|
||||
Related: []string{"1", "2", "3"},
|
||||
},
|
||||
types.ContentColumn{
|
||||
types.RecordColumn{
|
||||
Name: "description",
|
||||
Value: "persona non grata",
|
||||
},
|
||||
}
|
||||
|
||||
content2 := &types.Content{
|
||||
record2 := &types.Record{
|
||||
ModuleID: module.ID,
|
||||
}
|
||||
(&content2.Fields).Scan(func() []byte {
|
||||
(&record2.Fields).Scan(func() []byte {
|
||||
b, _ := json.Marshal(columns2)
|
||||
return b
|
||||
}())
|
||||
|
||||
// now work with content
|
||||
// now work with records
|
||||
{
|
||||
{
|
||||
m, err := repository.Update(content1)
|
||||
m, err := repository.Update(record1)
|
||||
assert(t, m == nil, "Expected empty return for invalid update, got %#v", m)
|
||||
assert(t, err != nil, "Expected error when updating invalid content")
|
||||
assert(t, err != nil, "Expected error when updating invalid record")
|
||||
}
|
||||
|
||||
// create content
|
||||
m1, err := repository.Create(content1)
|
||||
assert(t, err == nil, "Error when creating content: %+v", err)
|
||||
// create record
|
||||
m1, err := repository.Create(record1)
|
||||
assert(t, err == nil, "Error when creating record: %+v", err)
|
||||
assert(t, m1.ID > 0, "Expected auto generated ID")
|
||||
assert(t, m1.User != nil, "Expected non-nil user when creating content")
|
||||
assert(t, m1.User != nil, "Expected non-nil user when creating record")
|
||||
assert(t, m1.User.Username == "TestUser", "Expected 'TestUser' as username, got '%s'", m1.User.Username)
|
||||
|
||||
// create content
|
||||
m2, err := repository.Create(content2)
|
||||
assert(t, err == nil, "Error when creating content: %+v", err)
|
||||
// create record
|
||||
m2, err := repository.Create(record2)
|
||||
assert(t, err == nil, "Error when creating record: %+v", err)
|
||||
assert(t, m2.ID > 0, "Expected auto generated ID")
|
||||
assert(t, m2.User != nil, "Expected non-nil user when creating content")
|
||||
assert(t, m2.User != nil, "Expected non-nil user when creating record")
|
||||
assert(t, m2.User.Username == "TestUser", "Expected 'TestUser' as username, got '%s'", m2.User.Username)
|
||||
|
||||
// fetch created content
|
||||
// fetch created record
|
||||
{
|
||||
ms, err := repository.FindByID(m1.ID)
|
||||
assert(t, err == nil, "Error when retrieving content by id: %+v", err)
|
||||
assert(t, err == nil, "Error when retrieving record by id: %+v", err)
|
||||
assert(t, ms.ID == m1.ID, "Expected ID from database to match, %d != %d", m1.ID, ms.ID)
|
||||
assert(t, ms.ModuleID == m1.ModuleID, "Expected Module ID from database to match, %d != %d", m1.ModuleID, ms.ModuleID)
|
||||
|
||||
{
|
||||
fields, err := repository.Fields(ms)
|
||||
// fields := make([]testContentRow, 0)
|
||||
// fields := make([]testRecordRow, 0)
|
||||
// err = json.Unmarshal(ms.Fields, &fields)
|
||||
assert(t, err == nil, "%+v", errors.Wrap(err, "Didn't expect error when unmarshalling"))
|
||||
assert(t, len(fields) == len(columns), "Expected different field count: %d != %d", 2, len(fields))
|
||||
@@ -156,7 +156,7 @@ func TestContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
{
|
||||
fields := make([]types.ContentColumn, 0)
|
||||
fields := make([]types.RecordColumn, 0)
|
||||
err := json.Unmarshal(ms.Fields, &fields)
|
||||
assert(t, err == nil, "%+v", errors.Wrap(err, "Didn't expect error when unmarshalling"))
|
||||
assert(t, len(fields) == len(columns), "Expected different field count: %d != %d", 2, len(fields))
|
||||
@@ -166,58 +166,58 @@ func TestContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// update created content
|
||||
// update created record
|
||||
{
|
||||
_, err := repository.Update(m1)
|
||||
assert(t, err == nil, "Error when updating content, %+v", err)
|
||||
assert(t, err == nil, "Error when updating record, %+v", err)
|
||||
}
|
||||
|
||||
// re-fetch content
|
||||
// re-fetch record
|
||||
{
|
||||
ms, err := repository.FindByID(m1.ID)
|
||||
assert(t, err == nil, "Error when retrieving content by id: %+v", err)
|
||||
assert(t, err == nil, "Error when retrieving record by id: %+v", err)
|
||||
assert(t, ms.ID == m1.ID, "Expected ID from database to match, %d != %d", m1.ID, ms.ID)
|
||||
assert(t, ms.ModuleID == m1.ModuleID, "Expected ID from database to match, %d != %d", m1.ModuleID, ms.ModuleID)
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
// fetch all records
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "", 0, 20, "id desc")
|
||||
assert(t, err == nil, "Error when retrieving contents: %+v", err)
|
||||
assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents))
|
||||
assert(t, err == nil, "Error when retrieving records: %+v", err)
|
||||
assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records))
|
||||
assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count)
|
||||
assert(t, mr.Meta.Sort == "id desc", "Expected Meta.Sort == id desc, got '%s'", mr.Meta.Sort)
|
||||
assert(t, mr.Contents[0].ModuleID == m1.ModuleID, "Expected content module to match, %d != %d", m1.ModuleID, mr.Contents[0].ModuleID)
|
||||
assert(t, mr.Contents[0].ID > mr.Contents[1].ID, "Expected order to be descending")
|
||||
assert(t, mr.Records[0].ModuleID == m1.ModuleID, "Expected record module to match, %d != %d", m1.ModuleID, mr.Records[0].ModuleID)
|
||||
assert(t, mr.Records[0].ID > mr.Records[1].ID, "Expected order to be descending")
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
// fetch all records
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "", 0, 20, "name asc, email desc")
|
||||
assert(t, err == nil, "Error when retrieving contents: %+v", err)
|
||||
assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents))
|
||||
assert(t, err == nil, "Error when retrieving records: %+v", err)
|
||||
assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records))
|
||||
assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count)
|
||||
assert(t, mr.Meta.Sort == "name asc, email desc", "Expected Meta.Sort == 'name asc, email desc' '%s'", mr.Meta.Sort)
|
||||
assert(t, mr.Contents[0].ModuleID == m1.ModuleID, "Expected content module to match, %d != %d", m1.ModuleID, mr.Contents[0].ModuleID)
|
||||
assert(t, mr.Contents[0].ID > mr.Contents[1].ID, "Expected order to be ascending")
|
||||
assert(t, mr.Records[0].ModuleID == m1.ModuleID, "Expected record module to match, %d != %d", m1.ModuleID, mr.Records[0].ModuleID)
|
||||
assert(t, mr.Records[0].ID > mr.Records[1].ID, "Expected order to be ascending")
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
// fetch all records
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "", 0, 20, "created_at desc")
|
||||
assert(t, err == nil, "Error when retrieving contents: %+v", err)
|
||||
assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents))
|
||||
assert(t, err == nil, "Error when retrieving records: %+v", err)
|
||||
assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records))
|
||||
assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count)
|
||||
assert(t, mr.Meta.Sort == "created_at desc", "Expected Meta.Sort == created_at desc, got '%s'", mr.Meta.Sort)
|
||||
assert(t, mr.Contents[0].ModuleID == m1.ModuleID, "Expected content module to match, %d != %d", m1.ModuleID, mr.Contents[0].ModuleID)
|
||||
assert(t, mr.Contents[0].ID > mr.Contents[1].ID, "Expected order to be ascending")
|
||||
assert(t, mr.Records[0].ModuleID == m1.ModuleID, "Expected record module to match, %d != %d", m1.ModuleID, mr.Records[0].ModuleID)
|
||||
assert(t, mr.Records[0].ID > mr.Records[1].ID, "Expected order to be ascending")
|
||||
}
|
||||
|
||||
// fetch all contents by query
|
||||
// fetch all records by query
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "petric", 0, 20, "id desc")
|
||||
assert(t, err == nil, "Error when retrieving contents: %+v", err)
|
||||
assert(t, len(mr.Contents) == 1, "Expected one content, got %d", len(mr.Contents))
|
||||
assert(t, err == nil, "Error when retrieving records: %+v", err)
|
||||
assert(t, len(mr.Records) == 1, "Expected one record, got %d", len(mr.Records))
|
||||
assert(t, mr.Meta.Count == 1, "Expected Meta.Count == 1, got %d", mr.Meta.Count)
|
||||
assert(t, mr.Meta.Page == 0, "Expected Meta.Page == 0, got %d", mr.Meta.Page)
|
||||
assert(t, mr.Meta.PerPage == 20, "Expected Meta.PerPage == 20, got %d", mr.Meta.PerPage)
|
||||
@@ -225,27 +225,27 @@ func TestContent(t *testing.T) {
|
||||
assert(t, mr.Meta.Sort == "id desc", "Expected Meta.Sort == id desc, got '%s'", mr.Meta.Sort)
|
||||
}
|
||||
|
||||
// fetch all contents by query
|
||||
// fetch all records by query
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "niall", 0, 20, "id asc")
|
||||
assert(t, err == nil, "Error when retrieving contents: %+v", err)
|
||||
assert(t, len(mr.Contents) == 0, "Expected no contents, got %d", len(mr.Contents))
|
||||
assert(t, err == nil, "Error when retrieving records: %+v", err)
|
||||
assert(t, len(mr.Records) == 0, "Expected no records, got %d", len(mr.Records))
|
||||
}
|
||||
|
||||
// delete content
|
||||
// delete record
|
||||
{
|
||||
err := repository.DeleteByID(m1.ID)
|
||||
assert(t, err == nil, "Error when retrieving content by id: %+v", err)
|
||||
assert(t, err == nil, "Error when retrieving record by id: %+v", err)
|
||||
|
||||
err = repository.DeleteByID(m2.ID)
|
||||
assert(t, err == nil, "Error when retrieving content by id: %+v", err)
|
||||
assert(t, err == nil, "Error when retrieving record by id: %+v", err)
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
// fetch all records
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "", 0, 20, "")
|
||||
assert(t, err == nil, "Error when retrieving contents: %+v", err)
|
||||
assert(t, len(mr.Contents) == 0, "Expected no content, got %d", len(mr.Contents))
|
||||
assert(t, err == nil, "Error when retrieving records: %+v", err)
|
||||
assert(t, len(mr.Records) == 0, "Expected no record, got %d", len(mr.Records))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/crusttech/crust/crm/types"
|
||||
)
|
||||
|
||||
func (r *record) preloadAll(records []*types.Record, fields ...string) error {
|
||||
for _, record := range records {
|
||||
if err := r.preload(record, fields...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *record) preload(record *types.Record, fields ...string) (err error) {
|
||||
for _, field := range fields {
|
||||
switch field {
|
||||
case "fields":
|
||||
fields, err := r.Fields(record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json, err := json.Marshal(fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (&record.Fields).Scan(json); err != nil {
|
||||
return err
|
||||
}
|
||||
case "page":
|
||||
if record.Page, err = r.pageRepo.FindByModuleID(record.ModuleID); err != nil {
|
||||
return
|
||||
}
|
||||
case "user":
|
||||
if record.UserID > 0 {
|
||||
if record.User, err = r.userSvc.FindByID(record.UserID); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
var (
|
||||
o sync.Once
|
||||
DefaultContent ContentService
|
||||
DefaultRecord RecordService
|
||||
DefaultField FieldService
|
||||
DefaultModule ModuleService
|
||||
DefaultChart ChartService
|
||||
@@ -17,7 +17,7 @@ var (
|
||||
|
||||
func Init() {
|
||||
o.Do(func() {
|
||||
DefaultContent = Content()
|
||||
DefaultRecord = Record()
|
||||
DefaultField = Field()
|
||||
DefaultModule = Module()
|
||||
DefaultPage = Page()
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
ContentReport struct {
|
||||
Metrics []ContentReportMetric
|
||||
Dimensions []ContentReportDimensions
|
||||
}
|
||||
|
||||
ContentReportMetric struct {
|
||||
Alias string
|
||||
Expression string
|
||||
}
|
||||
|
||||
ContentReportDimensions struct {
|
||||
Alias string
|
||||
Field string
|
||||
Modifiers []string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
contentReportMetricScanRE *regexp.Regexp
|
||||
contentReportDimensionScanRE *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
contentReportMetricScanRE = regexp.MustCompile("^(?:(\\w+):)?(.+)$")
|
||||
contentReportDimensionScanRE = regexp.MustCompile("^(?:(\\w+):)?(\\w+)((?:\\|?\\w+)+)?$")
|
||||
}
|
||||
|
||||
func (r *ContentReport) ScanMetrics(metrics ...string) {
|
||||
r.Metrics = make([]ContentReportMetric, len(metrics))
|
||||
for i := 0; i < len(metrics); i++ {
|
||||
r.Metrics[i].Scan(metrics[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ContentReport) ScanDimensions(dimensions ...string) {
|
||||
r.Dimensions = make([]ContentReportDimensions, len(dimensions))
|
||||
for i := 0; i < len(dimensions); i++ {
|
||||
r.Dimensions[i].Scan(dimensions[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ContentReportMetric) Scan(metric string) {
|
||||
if match := contentReportMetricScanRE.FindStringSubmatch(metric); len(match) == 3 {
|
||||
m.Alias = match[1]
|
||||
m.Expression = match[2]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *ContentReportDimensions) Scan(dimension string) {
|
||||
if match := contentReportDimensionScanRE.FindStringSubmatch(dimension); len(match) == 4 {
|
||||
d.Alias = match[1]
|
||||
d.Field = match[2]
|
||||
|
||||
if len(match[3]) > 0 {
|
||||
d.Modifiers = strings.Split(match[3][1:], "|")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
RecordReport struct {
|
||||
Metrics []RecordReportMetric
|
||||
Dimensions []RecordReportDimensions
|
||||
}
|
||||
|
||||
RecordReportMetric struct {
|
||||
Alias string
|
||||
Expression string
|
||||
}
|
||||
|
||||
RecordReportDimensions struct {
|
||||
Alias string
|
||||
Field string
|
||||
Modifiers []string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
recordReportMetricScanRE *regexp.Regexp
|
||||
recordReportDimensionScanRE *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
recordReportMetricScanRE = regexp.MustCompile("^(?:(\\w+):)?(.+)$")
|
||||
recordReportDimensionScanRE = regexp.MustCompile("^(?:(\\w+):)?(\\w+)((?:\\|?\\w+)+)?$")
|
||||
}
|
||||
|
||||
func (r *RecordReport) ScanMetrics(metrics ...string) {
|
||||
r.Metrics = make([]RecordReportMetric, len(metrics))
|
||||
for i := 0; i < len(metrics); i++ {
|
||||
r.Metrics[i].Scan(metrics[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RecordReport) ScanDimensions(dimensions ...string) {
|
||||
r.Dimensions = make([]RecordReportDimensions, len(dimensions))
|
||||
for i := 0; i < len(dimensions); i++ {
|
||||
r.Dimensions[i].Scan(dimensions[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (m *RecordReportMetric) Scan(metric string) {
|
||||
if match := recordReportMetricScanRE.FindStringSubmatch(metric); len(match) == 3 {
|
||||
m.Alias = match[1]
|
||||
m.Expression = match[2]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RecordReportDimensions) Scan(dimension string) {
|
||||
if match := recordReportDimensionScanRE.FindStringSubmatch(dimension); len(match) == 4 {
|
||||
d.Alias = match[1]
|
||||
d.Field = match[2]
|
||||
|
||||
if len(match[3]) > 0 {
|
||||
d.Modifiers = strings.Split(match[3][1:], "|")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContentReport(t *testing.T) {
|
||||
r := &ContentReport{}
|
||||
func TestRecordReport(t *testing.T) {
|
||||
r := &RecordReport{}
|
||||
r.ScanMetrics("alias:exp")
|
||||
if len(r.Metrics) == 0 {
|
||||
t.Log("No metrics scanned")
|
||||
+13
-13
@@ -12,9 +12,9 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// Content is a stored row in the `content` table
|
||||
Content struct {
|
||||
ID uint64 `json:"contentID,string" db:"id"`
|
||||
// Record is a stored row in the `record` table
|
||||
Record struct {
|
||||
ID uint64 `json:"recordID,string" db:"id"`
|
||||
ModuleID uint64 `json:"moduleID,string" db:"module_id"`
|
||||
|
||||
User *systemTypes.User `json:"user,omitempty" db:"-"`
|
||||
@@ -29,20 +29,20 @@ type (
|
||||
DeletedAt *time.Time `db:"deleted_at" json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
// ContentColumn is a stored row in the `content_column` table
|
||||
ContentColumn struct {
|
||||
ContentID uint64 `json:"-" db:"content_id"`
|
||||
Name string `json:"name" db:"column_name"`
|
||||
Value string `json:"value" db:"column_value"`
|
||||
Related []string `json:"related" db:"-"`
|
||||
// RecordColumn is a stored row in the `record_column` table
|
||||
RecordColumn struct {
|
||||
RecordID uint64 `json:"-" db:"record_id"`
|
||||
Name string `json:"name" db:"column_name"`
|
||||
Value string `json:"value" db:"column_value"`
|
||||
Related []string `json:"related" db:"-"`
|
||||
}
|
||||
|
||||
Related struct {
|
||||
ContentID uint64 `json:"-" db:"content_id"`
|
||||
Name string `json:"-" db:"column_name"`
|
||||
RecordID uint64 `json:"-" db:"record_id"`
|
||||
Name string `json:"-" db:"column_name"`
|
||||
|
||||
// RelatedContentID isn't necessarily a content ID (multiple-select anything goes options)
|
||||
RelatedContentID string `json:"-" db:"rel_content_id"`
|
||||
// RelatedRecordID isn't necessarily a record ID (multiple-select anything goes options)
|
||||
RelatedRecordID string `json:"-" db:"rel_record_id"`
|
||||
}
|
||||
|
||||
// Field - CRM input field definitions
|
||||
|
||||
+15
-15
@@ -314,13 +314,13 @@ CRM module definitions
|
||||
| dimensions | string | GET | Dimensions (syntax: alias:field|modifier|modifier2;...) | N/A | YES |
|
||||
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||
|
||||
## List/read contents from module section
|
||||
## List/read records from module section
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/module/{moduleID}/content` | HTTP/S | GET | |
|
||||
| `/module/{moduleID}/record` | HTTP/S | GET | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
@@ -332,66 +332,66 @@ CRM module definitions
|
||||
| sort | string | GET | Sort field (default id desc) | N/A | NO |
|
||||
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||
|
||||
## List/read contents from module section
|
||||
## Create record in module section
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/module/{moduleID}/content` | HTTP/S | POST | |
|
||||
| `/module/{moduleID}/record` | HTTP/S | POST | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||
| fields | sqlxTypes.JSONText | POST | Content JSON | N/A | YES |
|
||||
| fields | sqlxTypes.JSONText | POST | Record JSON | N/A | YES |
|
||||
|
||||
## Read contents by ID from module section
|
||||
## Read records by ID from module section
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/module/{moduleID}/content/{contentID}` | HTTP/S | GET | |
|
||||
| `/module/{moduleID}/record/{recordID}` | HTTP/S | GET | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||
| contentID | uint64 | PATH | Content ID | N/A | YES |
|
||||
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||
|
||||
## Add/update contents in module section
|
||||
## Add/update records in module section
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/module/{moduleID}/content/{contentID}` | HTTP/S | POST | |
|
||||
| `/module/{moduleID}/record/{recordID}` | HTTP/S | POST | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||
| contentID | uint64 | PATH | Content ID | N/A | YES |
|
||||
| fields | sqlxTypes.JSONText | POST | Content JSON | N/A | YES |
|
||||
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||
| fields | sqlxTypes.JSONText | POST | Record JSON | N/A | YES |
|
||||
|
||||
## Delete content row from module section
|
||||
## Delete record row from module section
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/module/{moduleID}/content/{contentID}` | HTTP/S | DELETE | |
|
||||
| `/module/{moduleID}/record/{recordID}` | HTTP/S | DELETE | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||
| contentID | uint64 | PATH | Content ID | N/A | YES |
|
||||
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user