add(crm): content order params
This commit is contained in:
committed by
Denis Arh
parent
9d62e6090e
commit
dc2635e05c
@@ -454,6 +454,12 @@
|
||||
"type": "int",
|
||||
"required": false,
|
||||
"title": "Returned items per page (default 50)"
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"title": "Sort field (default id desc)"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -218,6 +218,12 @@
|
||||
"required": false,
|
||||
"title": "Returned items per page (default 50)",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"required": false,
|
||||
"title": "Sort field (default id desc)",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
|
||||
@@ -20,7 +20,7 @@ type (
|
||||
|
||||
FindByID(id uint64) (*types.Content, error)
|
||||
|
||||
Find(moduleID uint64, query string, page int, perPage int) (*FindResponse, 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)
|
||||
@@ -34,6 +34,7 @@ type (
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
Count int `json:"count"`
|
||||
Sort string `json:"sort"`
|
||||
}
|
||||
|
||||
FindResponse struct {
|
||||
@@ -66,7 +67,7 @@ func (r *content) FindByID(id uint64) (*types.Content, error) {
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func (r *content) Find(moduleID uint64, query string, page int, perPage int) (*FindResponse, error) {
|
||||
func (r *content) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) {
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
@@ -84,6 +85,7 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int) (*F
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
Query: query,
|
||||
Sort: sort,
|
||||
},
|
||||
Contents: make([]*types.Content, 0),
|
||||
}
|
||||
@@ -93,10 +95,87 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int) (*F
|
||||
sqlSelect := "SELECT * FROM crm_content"
|
||||
sqlCount := "SELECT count(*) FROM crm_content"
|
||||
sqlWhere := "WHERE module_id=? and deleted_at IS NULL"
|
||||
|
||||
sqlOrder := "ORDER BY id DESC"
|
||||
sqlLimit := fmt.Sprintf("LIMIT %d, %d", page*perPage, perPage)
|
||||
|
||||
chuncks := strings.Split(sort, ",")
|
||||
if len(chuncks) > 0 {
|
||||
|
||||
// Ger module fields.
|
||||
modulRepo := Module(r.Context(), r.db())
|
||||
mod, err := modulRepo.FindByID(moduleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modFields, err := modulRepo.FieldNames(mod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldMap := make(map[string]bool)
|
||||
for i := 0; i < len(modFields); i++ {
|
||||
fieldMap[modFields[i]] = true
|
||||
}
|
||||
|
||||
orderFields := make([]string, 0)
|
||||
for _, c := range chuncks {
|
||||
args := strings.Split(c, " ")
|
||||
|
||||
var field string
|
||||
if _, ok := fieldMap[args[0]]; ok {
|
||||
field = "JSON_UNQUOTE(JSON_EXTRACT(json, REPLACE(JSON_UNQUOTE(JSON_SEARCH(json, 'one', '" + args[0] + "')), '.name', '.value')))"
|
||||
} else {
|
||||
switch args[0] {
|
||||
case "moduleId":
|
||||
field = "module_id"
|
||||
case "userId":
|
||||
field = "user_id"
|
||||
case "createdAt":
|
||||
field = "created_at"
|
||||
case "updatedAt":
|
||||
field = "updated_at"
|
||||
case "deletedAt":
|
||||
field = "deleted_at"
|
||||
default:
|
||||
field = "id"
|
||||
}
|
||||
}
|
||||
|
||||
// Check for second order parameter or use default value ASC.
|
||||
order := "ASC"
|
||||
if len(args) == 2 {
|
||||
order = strings.ToUpper(args[1])
|
||||
switch order {
|
||||
case "DESC":
|
||||
order = "DESC"
|
||||
default:
|
||||
order = "ASC"
|
||||
}
|
||||
}
|
||||
|
||||
// We skip batch of parameters if there are more then 2 values.
|
||||
if len(args) > 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add field and order to sort order fields.
|
||||
orderFields = append(orderFields, field+" "+order)
|
||||
}
|
||||
|
||||
sqlOrder = "ORDER BY " + strings.Join(orderFields, ", ")
|
||||
}
|
||||
|
||||
// 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"
|
||||
// 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
|
||||
// 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 ?)"
|
||||
@@ -110,7 +189,7 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int) (*F
|
||||
if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.db().Select(&response.Contents, fmt.Sprintf("SELECT * FROM crm_content WHERE module_id=? and deleted_at IS NULL ORDER BY id DESC LIMIT %d, %d", page, perPage), moduleID); err != nil {
|
||||
if err := r.db().Select(&response.Contents, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ func (s *Module) Edit(ctx context.Context, r *request.ModuleEdit) (interface{},
|
||||
}
|
||||
|
||||
func (s *Module) ContentList(ctx context.Context, r *request.ModuleContentList) (interface{}, error) {
|
||||
return s.content.With(ctx).Find(r.ModuleID, r.Query, r.Page, r.PerPage)
|
||||
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) {
|
||||
|
||||
@@ -358,6 +358,7 @@ type ModuleContentList struct {
|
||||
Query string
|
||||
Page int
|
||||
PerPage int
|
||||
Sort string
|
||||
ModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
@@ -404,6 +405,10 @@ func (m *ModuleContentList) Fill(r *http.Request) (err error) {
|
||||
|
||||
m.PerPage = parseInt(val)
|
||||
}
|
||||
if val, ok := get["sort"]; ok {
|
||||
|
||||
m.Sort = val
|
||||
}
|
||||
m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID"))
|
||||
|
||||
return err
|
||||
|
||||
@@ -28,7 +28,7 @@ type (
|
||||
|
||||
FindByID(contentID uint64) (*types.Content, error)
|
||||
|
||||
Find(moduleID uint64, query string, page int, perPage int) (*repository.FindResponse, 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)
|
||||
@@ -63,8 +63,8 @@ func (s *content) FindByID(id uint64) (*types.Content, error) {
|
||||
return response, s.preload(response, "page", "user", "fields")
|
||||
}
|
||||
|
||||
func (s *content) Find(moduleID uint64, query string, page int, perPage int) (*repository.FindResponse, error) {
|
||||
response, err := s.repository.Find(moduleID, query, page, perPage)
|
||||
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
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (s *content) Create(mod *types.Content) (*types.Content, error) {
|
||||
|
||||
func (s *content) Update(mod *types.Content) (*types.Content, error) {
|
||||
if mod.ID == 0 {
|
||||
return nil, errors.New("Error when savig content, invalid ID")
|
||||
return nil, errors.New("Error when saving content, invalid ID")
|
||||
}
|
||||
return s.repository.Update(mod)
|
||||
}
|
||||
|
||||
+94
-25
@@ -48,6 +48,10 @@ func TestContent(t *testing.T) {
|
||||
Name: "options",
|
||||
Kind: "select_multi",
|
||||
},
|
||||
types.ModuleField{
|
||||
Name: "description",
|
||||
Kind: "text",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -71,37 +75,75 @@ func TestContent(t *testing.T) {
|
||||
Name: "options",
|
||||
Related: []string{"1", "2", "3"},
|
||||
},
|
||||
types.ContentColumn{
|
||||
Name: "description",
|
||||
Value: "jack of all trades",
|
||||
},
|
||||
}
|
||||
|
||||
content := &types.Content{
|
||||
content1 := &types.Content{
|
||||
ModuleID: module.ID,
|
||||
}
|
||||
(&content.Fields).Scan(func() []byte {
|
||||
(&content1.Fields).Scan(func() []byte {
|
||||
b, _ := json.Marshal(columns)
|
||||
return b
|
||||
}())
|
||||
|
||||
columns2 := []types.ContentColumn{
|
||||
types.ContentColumn{
|
||||
Name: "name",
|
||||
Value: "Marko Novak",
|
||||
},
|
||||
types.ContentColumn{
|
||||
Name: "email",
|
||||
Value: "marko.n@example.com",
|
||||
},
|
||||
types.ContentColumn{
|
||||
Name: "options",
|
||||
Related: []string{"1", "2", "3"},
|
||||
},
|
||||
types.ContentColumn{
|
||||
Name: "description",
|
||||
Value: "persona non grata",
|
||||
},
|
||||
}
|
||||
|
||||
content2 := &types.Content{
|
||||
ModuleID: module.ID,
|
||||
}
|
||||
(&content2.Fields).Scan(func() []byte {
|
||||
b, _ := json.Marshal(columns2)
|
||||
return b
|
||||
}())
|
||||
|
||||
// now work with content
|
||||
{
|
||||
{
|
||||
m, err := repository.Update(content)
|
||||
assert(t, m == nil, "Expected empty return for ivalid update, got %#v", m)
|
||||
m, err := repository.Update(content1)
|
||||
assert(t, m == nil, "Expected empty return for invalid update, got %#v", m)
|
||||
assert(t, err != nil, "Expected error when updating invalid content")
|
||||
}
|
||||
|
||||
// create content
|
||||
m, err := repository.Create(content)
|
||||
m1, err := repository.Create(content1)
|
||||
assert(t, err == nil, "Error when creating content: %+v", err)
|
||||
assert(t, m.ID > 0, "Expected auto generated ID")
|
||||
assert(t, m.User != nil, "Expected non-nil user when creating content")
|
||||
assert(t, m.User.Username == "TestUser", "Expected 'TestUser' as username, got '%s'", m.User.Username)
|
||||
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.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)
|
||||
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.Username == "TestUser", "Expected 'TestUser' as username, got '%s'", m2.User.Username)
|
||||
|
||||
// fetch created content
|
||||
{
|
||||
ms, err := repository.FindByID(m.ID)
|
||||
ms, err := repository.FindByID(m1.ID)
|
||||
assert(t, err == nil, "Error when retrieving content 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.ModuleID == m.ModuleID, "Expected Module ID from database to match, %d != %d", m.ModuleID, ms.ModuleID)
|
||||
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)
|
||||
@@ -126,57 +168,84 @@ func TestContent(t *testing.T) {
|
||||
|
||||
// update created content
|
||||
{
|
||||
_, err := repository.Update(m)
|
||||
_, err := repository.Update(m1)
|
||||
assert(t, err == nil, "Error when updating content, %+v", err)
|
||||
}
|
||||
|
||||
// re-fetch content
|
||||
{
|
||||
ms, err := repository.FindByID(m.ID)
|
||||
ms, err := repository.FindByID(m1.ID)
|
||||
assert(t, err == nil, "Error when retrieving content 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.ModuleID == m.ModuleID, "Expected ID from database to match, %d != %d", m.ModuleID, ms.ModuleID)
|
||||
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
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "", 0, 20)
|
||||
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) == 1, "Expected one content, got %d", len(mr.Contents))
|
||||
assert(t, mr.Meta.Count == 1, "Expected Meta.Count == 1, got %d", mr.Meta.Count)
|
||||
assert(t, mr.Contents[0].ModuleID == m.ModuleID, "Expected content module to match, %d != %d", m.ModuleID, mr.Contents[0].ModuleID)
|
||||
assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents))
|
||||
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")
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
{
|
||||
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, 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")
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
{
|
||||
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, 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")
|
||||
}
|
||||
|
||||
// fetch all contents by query
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "petric", 0, 20)
|
||||
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, 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)
|
||||
assert(t, mr.Meta.Query == "petric", "Expected Meta.Query == petric, got '%s'", mr.Meta.Query)
|
||||
assert(t, mr.Meta.Sort == "id desc", "Expected Meta.Sort == id desc, got '%s'", mr.Meta.Sort)
|
||||
}
|
||||
|
||||
// fetch all contents by query
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "niall", 0, 20)
|
||||
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))
|
||||
}
|
||||
|
||||
// re-fetch content
|
||||
// delete content
|
||||
{
|
||||
err := repository.DeleteByID(m.ID)
|
||||
err := repository.DeleteByID(m1.ID)
|
||||
assert(t, err == nil, "Error when retrieving content by id: %+v", err)
|
||||
|
||||
err = repository.DeleteByID(m2.ID)
|
||||
assert(t, err == nil, "Error when retrieving content by id: %+v", err)
|
||||
}
|
||||
|
||||
// fetch all contents
|
||||
{
|
||||
mr, err := repository.Find(module.ID, "", 0, 20)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestPage(t *testing.T) {
|
||||
{
|
||||
{
|
||||
m, err := repository.Update(page)
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
@@ -333,6 +333,7 @@ Example bar chart with number of leads per country: select lead.country from lea
|
||||
| query | string | GET | Search query | N/A | NO |
|
||||
| page | int | GET | Page number (0 based) | N/A | NO |
|
||||
| perPage | int | GET | Returned items per page (default 50) | N/A | NO |
|
||||
| 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
|
||||
|
||||
Reference in New Issue
Block a user