Add record procedure exec capabilities

This will allow us to perform bulk operations on record & record-values
This commit is contained in:
Denis Arh
2019-09-13 11:29:01 +02:00
parent 05dfa30500
commit dc99bc2cb8
14 changed files with 565 additions and 53 deletions
+5
View File
@@ -121,6 +121,11 @@ func newHelper(t *testing.T) helper {
return h
}
// Returns context w/ security details
func (h helper) secCtx() context.Context {
return auth.SetIdentityToContext(context.Background(), h.cUser)
}
// apitest basics, initialize, set handler, add auth
func (h helper) apiInit() *apitest.APITest {
InitApp()
+2 -2
View File
@@ -17,10 +17,10 @@ func (h helper) repoModule() repository.ModuleRepository {
return repository.Module(context.Background(), db())
}
func (h helper) repoMakeModule(ns *types.Namespace, name string) *types.Module {
func (h helper) repoMakeModule(ns *types.Namespace, name string, ff ...*types.ModuleField) *types.Module {
m, err := h.
repoModule().
Create(&types.Module{Name: name, NamespaceID: ns.ID})
Create(&types.Module{Name: name, NamespaceID: ns.ID, Fields: ff})
h.a.NoError(err)
return m
+161
View File
@@ -0,0 +1,161 @@
package compose
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"testing"
"github.com/steinfletcher/apitest"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/compose/service"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/rh"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func (h helper) apiSendRecordExec(nsID, modID uint64, proc string, args []request.ProcedureArg) *apitest.Response {
payload, err := json.Marshal(request.RecordExec{Args: args})
h.a.NoError(err)
return h.apiInit().
Post(fmt.Sprintf("/namespace/%d/module/%d/record/exec/%s", nsID, modID, proc)).
JSON(string(payload)).
Expect(h.t)
}
func TestRecordExecUnknownProcedure(t *testing.T) {
h := newHelper(t)
h.apiInit().
Post("/namespace/0/module/0/record/exec/test-unexisting-proc").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("unknown procedure")).
End()
}
func TestRecordExec(t *testing.T) {
h := newHelper(t)
h.allow(types.ModulePermissionResource.AppendWildcard(), "record.update")
module := h.repoMakeRecordModuleWithFields(
"record testing module",
&types.ModuleField{Name: "position", Kind: "Number"},
&types.ModuleField{Name: "handle"},
&types.ModuleField{Name: "category"},
)
makeRecord := func(position int, handle, cat string) *types.Record {
return h.repoMakeRecord(module,
&types.RecordValue{Name: "position", Value: strconv.Itoa(position)},
&types.RecordValue{Name: "handle", Value: handle},
&types.RecordValue{Name: "category", Value: cat},
)
}
assertSort := func(expectedHandles string) {
// Using record service for fetching to avoid value pre-fetching etc..
set, _, err := service.DefaultRecord.With(h.secCtx()).Find(types.RecordFilter{
ModuleID: module.ID,
NamespaceID: module.NamespaceID,
Sort: "position ASC",
PageFilter: rh.PageFilter{},
})
h.a.NoError(err)
h.a.NotNil(set)
actualHandles := ""
_ = set.Walk(func(r *types.Record) error {
v := r.Values.FilterByName("handle")
if len(v) == 1 {
actualHandles += v[0].Value
} else {
actualHandles += strconv.Itoa(len(v))
}
return nil
})
h.a.Equal(expectedHandles, actualHandles)
}
var (
aRec = makeRecord(1, "a", "CAT1")
bRec = makeRecord(2, "b", "CAT1")
cRec = makeRecord(3, "c", "CAT1")
dRec = makeRecord(4, "d", "CAT2")
eRec = makeRecord(5, "e", "CAT2")
fRec = makeRecord(6, "f", "CAT2")
gRec = makeRecord(7, "g", "CAT3")
hRec = makeRecord(8, "h", "CAT3")
iRec = makeRecord(9, "i", "CAT3")
)
// map handle to record ID so we can use it for reordering
rr := map[string]string{
"a": strconv.FormatUint(aRec.ID, 10),
"b": strconv.FormatUint(bRec.ID, 10),
"c": strconv.FormatUint(cRec.ID, 10),
"d": strconv.FormatUint(dRec.ID, 10),
"e": strconv.FormatUint(eRec.ID, 10),
"f": strconv.FormatUint(fRec.ID, 10),
"g": strconv.FormatUint(gRec.ID, 10),
"h": strconv.FormatUint(hRec.ID, 10),
"i": strconv.FormatUint(iRec.ID, 10),
}
assertSort("abcdefghi")
h.apiSendRecordExec(module.NamespaceID, module.ID, "organize", request.ProcedureArgs{
{"recordID", rr["a"]},
{"sortingField", "position"},
{"sortingValue", "5"}}).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
assertSort("bcdeafghi")
h.apiSendRecordExec(module.NamespaceID, module.ID, "organize", request.ProcedureArgs{
{"recordID", rr["i"]},
{"sortingField", "position"},
{"sortingValue", "0"}}).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
assertSort("ibcdeafgh")
h.apiSendRecordExec(module.NamespaceID, module.ID, "organize", request.ProcedureArgs{
{"recordID", rr["b"]},
{"sortingFilter", "category = 'CAT1'"},
{"sortingField", "position"},
{"sortingValue", "5"}}).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
assertSort("idecbfgah")
h.apiSendRecordExec(module.NamespaceID, module.ID, "organize", request.ProcedureArgs{
{"recordID", rr["b"]},
{"valueField", "category"},
{"value", "CAT2"},
{"sortingValue", "5"}}).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
assertSort("idecbfgah")
rsv, err := h.repoRecord().LoadValues([]string{"category"}, []uint64{bRec.ID})
h.a.NoError(err)
h.a.NotNil(rsv)
h.a.Len(rsv.FilterByName("category"), 1)
h.a.Equal("CAT2", rsv.FilterByName("category")[0].Value)
}
+36 -38
View File
@@ -18,45 +18,40 @@ func (h helper) repoRecord() repository.RecordRepository {
return repository.Record(context.Background(), db())
}
func (h helper) repoMakeRecordModuleWithFields(name string) *types.Module {
func (h helper) repoMakeRecordModuleWithFields(name string, ff ...*types.ModuleField) *types.Module {
namespace := h.repoMakeNamespace("record testing namespace")
h.allow(types.NamespacePermissionResource.AppendWildcard(), "read")
h.allow(types.ModulePermissionResource.AppendWildcard(), "read")
h.allow(types.ModulePermissionResource.AppendWildcard(), "record.read")
m, err := h.
repoModule().
Create(&types.Module{
Name: name,
NamespaceID: namespace.ID,
Fields: types.ModuleFieldSet{
&types.ModuleField{
Name: "name",
},
&types.ModuleField{
Name: "email",
},
&types.ModuleField{
Name: "options",
Multi: true,
},
&types.ModuleField{
Name: "description",
},
&types.ModuleField{
Name: "another_record",
Kind: "Record",
},
if len(ff) == 0 {
// Default fields
ff = types.ModuleFieldSet{
&types.ModuleField{
Name: "name",
},
})
&types.ModuleField{
Name: "email",
},
&types.ModuleField{
Name: "options",
Multi: true,
},
&types.ModuleField{
Name: "description",
},
&types.ModuleField{
Name: "another_record",
Kind: "Record",
},
}
}
h.a.NoError(err)
return m
return h.repoMakeModule(namespace, name, ff...)
}
func (h helper) repoMakeRecord(module *types.Module, name string) *types.Record {
func (h helper) repoMakeRecord(module *types.Module, rvs ...*types.RecordValue) *types.Record {
record, err := h.
repoRecord().
Create(&types.Record{
@@ -68,6 +63,9 @@ func (h helper) repoMakeRecord(module *types.Module, name string) *types.Record
})
h.a.NoError(err)
err = h.repoRecord().UpdateValues(record.ID, rvs)
h.a.NoError(err)
return record
}
@@ -75,7 +73,7 @@ func TestRecordRead(t *testing.T) {
h := newHelper(t)
module := h.repoMakeRecordModuleWithFields("record testing module")
record := h.repoMakeRecord(module, "some-record")
record := h.repoMakeRecord(module)
h.apiInit().
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", module.NamespaceID, module.ID, record.ID)).
@@ -91,8 +89,8 @@ func TestRecordList(t *testing.T) {
module := h.repoMakeRecordModuleWithFields("record testing module")
h.repoMakeRecord(module, "app")
h.repoMakeRecord(module, "app")
h.repoMakeRecord(module)
h.repoMakeRecord(module)
h.apiInit().
Get(fmt.Sprintf("/namespace/%d/module/%d/record/", module.NamespaceID, module.ID)).
@@ -109,7 +107,7 @@ func TestRecordCreateForbidden(t *testing.T) {
h.apiInit().
Post(fmt.Sprintf("/namespace/%d/module/%d/record/", module.NamespaceID, module.ID)).
FormData("name", "some-record").
FormData("name").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("compose.service.NoCreatePermissions")).
@@ -124,7 +122,7 @@ func TestRecordCreate(t *testing.T) {
h.apiInit().
Post(fmt.Sprintf("/namespace/%d/module/%d/record/", module.NamespaceID, module.ID)).
FormData("name", "some-record").
FormData("name").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
@@ -135,7 +133,7 @@ func TestRecordUpdateForbidden(t *testing.T) {
h := newHelper(t)
module := h.repoMakeRecordModuleWithFields("record testing module")
record := h.repoMakeRecord(module, "some-record")
record := h.repoMakeRecord(module)
h.apiInit().
Post(fmt.Sprintf("/namespace/%d/module/%d/record/%d", module.NamespaceID, module.ID, record.ID)).
@@ -150,7 +148,7 @@ func TestRecordUpdate(t *testing.T) {
h := newHelper(t)
module := h.repoMakeRecordModuleWithFields("record testing module")
record := h.repoMakeRecord(module, "some-record")
record := h.repoMakeRecord(module)
h.allow(types.ModulePermissionResource.AppendWildcard(), "record.update")
h.apiInit().
@@ -171,7 +169,7 @@ func TestRecordDeleteForbidden(t *testing.T) {
h := newHelper(t)
module := h.repoMakeRecordModuleWithFields("record testing module")
record := h.repoMakeRecord(module, "some-record")
record := h.repoMakeRecord(module)
h.apiInit().
Delete(fmt.Sprintf("/namespace/%d/module/%d/record/%d", module.NamespaceID, module.ID, record.ID)).
@@ -185,7 +183,7 @@ func TestRecordDelete(t *testing.T) {
h := newHelper(t)
module := h.repoMakeRecordModuleWithFields("record testing module")
record := h.repoMakeRecord(module, "some-record")
record := h.repoMakeRecord(module)
h.allow(types.ModulePermissionResource.AppendWildcard(), "record.delete")