Update Envoy for new/extended resources
* Reports * API GW * Module field; user role filter * Comment page block
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
APIGateway struct {
|
||||
*base
|
||||
Res *types.ApigwRoute
|
||||
|
||||
Filters []*APIGatewayFilter
|
||||
}
|
||||
|
||||
APIGatewayFilter struct {
|
||||
*base
|
||||
Res *types.ApigwFilter
|
||||
}
|
||||
)
|
||||
|
||||
func NewAPIGateway(res *types.ApigwRoute) *APIGateway {
|
||||
r := &APIGateway{
|
||||
base: &base{},
|
||||
}
|
||||
r.SetResourceType(types.ApigwRouteResourceType)
|
||||
r.Res = res
|
||||
|
||||
r.AddIdentifier(identifiers(res.ID)...)
|
||||
|
||||
// Initial stamps
|
||||
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
|
||||
us := MakeUserstampsCUDO(res.CreatedBy, res.UpdatedBy, res.DeletedBy, 0)
|
||||
r.SetUserstamps(us)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *APIGateway) AddGatewayFilter(res *types.ApigwFilter) *APIGatewayFilter {
|
||||
f := &APIGatewayFilter{
|
||||
base: &base{},
|
||||
}
|
||||
|
||||
f.Res = res
|
||||
|
||||
// Initial stamps
|
||||
f.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
|
||||
f.SetUserstamps(MakeUserstampsCUDO(res.CreatedBy, res.UpdatedBy, res.DeletedBy, 0))
|
||||
|
||||
r.Filters = append(r.Filters, f)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (r *APIGateway) SysID() uint64 {
|
||||
return r.Res.ID
|
||||
}
|
||||
|
||||
// FindAPIGateway looks for the ApigwRoute in the resource set
|
||||
func FindAPIGateway(rr InterfaceSet, ii Identifiers) (ns *types.ApigwRoute) {
|
||||
var wfRes *APIGateway
|
||||
|
||||
rr.Walk(func(r Interface) error {
|
||||
wr, ok := r.(*APIGateway)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if wr.Identifiers().HasAny(ii) {
|
||||
wfRes = wr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Found it
|
||||
if wfRes != nil {
|
||||
return wfRes.Res
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func APIGatewayErrUnresolved(ii Identifiers) error {
|
||||
return fmt.Errorf("automation apu gateway unresolved %v", ii.StringSlice())
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/minions"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -24,8 +25,9 @@ type (
|
||||
ResFields []*ComposeModuleField
|
||||
|
||||
// Might keep track of related NS
|
||||
RefNs *Ref
|
||||
RefMods RefSet
|
||||
RefNs *Ref
|
||||
RefMods RefSet
|
||||
RefRoles RefSet
|
||||
}
|
||||
)
|
||||
|
||||
@@ -52,6 +54,19 @@ func NewComposeModule(res *types.Module, nsRef string) *ComposeModule {
|
||||
if refMod != "" && refMod != "0" {
|
||||
r.RefMods = append(r.RefMods, r.AddRef(types.ModuleResourceType, refMod).Constraint(r.RefNs))
|
||||
}
|
||||
|
||||
case "User":
|
||||
refRoles := ComposeModuleFieldExtractUserFieldRoles(f.Options["roles"])
|
||||
if len(refRoles) == 0 {
|
||||
refRoles = ComposeModuleFieldExtractUserFieldRoles(f.Options["role"])
|
||||
}
|
||||
if len(refRoles) == 0 {
|
||||
refRoles = ComposeModuleFieldExtractUserFieldRoles(f.Options["roleID"])
|
||||
}
|
||||
|
||||
for _, refRole := range refRoles {
|
||||
r.RefRoles = append(r.RefRoles, r.AddRef(systemTypes.RoleResourceType, refRole))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,3 +219,67 @@ func NewComposeModuleField(res *types.ModuleField, nsRef, modRef string) *Compos
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// ComposeModuleFieldExtractUserFieldRoles is a helper to extract roles
|
||||
// from the given filer options.
|
||||
func ComposeModuleFieldExtractUserFieldRoles(i interface{}) []string {
|
||||
if minions.IsNil(i) {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]string, 0, 1)
|
||||
|
||||
isOk := func(v string) bool {
|
||||
return v != "" && v != "0"
|
||||
}
|
||||
|
||||
switch v := i.(type) {
|
||||
case uint64:
|
||||
aux := strconv.FormatUint(v, 10)
|
||||
if !isOk(aux) {
|
||||
return nil
|
||||
}
|
||||
return []string{aux}
|
||||
case []uint64:
|
||||
for _, i := range v {
|
||||
aux := strconv.FormatUint(i, 10)
|
||||
if !isOk(aux) {
|
||||
continue
|
||||
}
|
||||
out = append(out, aux)
|
||||
}
|
||||
return out
|
||||
|
||||
case string:
|
||||
if !isOk(v) {
|
||||
return nil
|
||||
}
|
||||
return []string{v}
|
||||
case []string:
|
||||
for _, aux := range v {
|
||||
if !isOk(aux) {
|
||||
continue
|
||||
}
|
||||
out = append(out, aux)
|
||||
}
|
||||
return out
|
||||
|
||||
case []interface{}:
|
||||
for _, i := range v {
|
||||
aux := cast.ToString(i)
|
||||
if !isOk(aux) {
|
||||
continue
|
||||
}
|
||||
out = append(out, aux)
|
||||
}
|
||||
return out
|
||||
case interface{}:
|
||||
aux := cast.ToString(v)
|
||||
if !isOk(aux) {
|
||||
return nil
|
||||
}
|
||||
return []string{aux}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -129,6 +129,14 @@ func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePag
|
||||
r.ModRefs = append(r.ModRefs, ref)
|
||||
}
|
||||
}
|
||||
|
||||
case "Comment":
|
||||
id := ss(b.Options, "module", "moduleID")
|
||||
if id != "" {
|
||||
ref := r.AddRef(types.ModuleResourceType, id).Constraint(r.RefNs)
|
||||
r.BlockRefs[i] = add(r.BlockRefs[i], ref)
|
||||
r.ModRefs = append(r.ModRefs, ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
apiGateway struct {
|
||||
cfg *EncoderConfig
|
||||
|
||||
res *resource.APIGateway
|
||||
gwr *types.ApigwRoute
|
||||
ff types.ApigwFilterSet
|
||||
|
||||
ux *userIndex
|
||||
}
|
||||
apiGatewaySet []*apiGateway
|
||||
|
||||
apiGatewayFilter struct {
|
||||
cfg *EncoderConfig
|
||||
|
||||
res *resource.APIGatewayFilter
|
||||
tr *types.ApigwFilter
|
||||
}
|
||||
apiGatewayFilterSet []*apiGatewayFilter
|
||||
)
|
||||
|
||||
// mergeAPIGateways merges b into a, prioritising a
|
||||
func mergeAPIGateways(a, b *types.ApigwRoute) *types.ApigwRoute {
|
||||
c := a
|
||||
|
||||
if c.Endpoint == "" {
|
||||
c.Endpoint = b.Endpoint
|
||||
}
|
||||
if c.Method == "" {
|
||||
c.Method = b.Method
|
||||
}
|
||||
|
||||
c.Enabled = b.Enabled
|
||||
|
||||
if c.Group == 0 {
|
||||
c.Group = b.Group
|
||||
}
|
||||
|
||||
c.Meta = b.Meta
|
||||
|
||||
if c.CreatedBy == 0 {
|
||||
c.CreatedBy = b.CreatedBy
|
||||
}
|
||||
if c.UpdatedBy == 0 {
|
||||
c.UpdatedBy = b.UpdatedBy
|
||||
}
|
||||
if c.DeletedBy == 0 {
|
||||
c.DeletedBy = b.DeletedBy
|
||||
}
|
||||
|
||||
if c.CreatedAt.IsZero() {
|
||||
c.CreatedAt = b.CreatedAt
|
||||
}
|
||||
|
||||
if c.UpdatedAt == nil {
|
||||
c.UpdatedAt = b.UpdatedAt
|
||||
}
|
||||
|
||||
if c.DeletedAt == nil {
|
||||
c.DeletedAt = b.DeletedAt
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// findAPIGateway looks for the workflow in the resources & the store
|
||||
//
|
||||
// Provided resources are prioritized.
|
||||
func findAPIGateway(ctx context.Context, s store.Storer, rr resource.InterfaceSet, ii resource.Identifiers) (wf *types.ApigwRoute, err error) {
|
||||
wf = resource.FindAPIGateway(rr, ii)
|
||||
if wf != nil {
|
||||
return wf, nil
|
||||
}
|
||||
|
||||
return findAPIGatewayStore(ctx, s, makeGenericFilter(ii))
|
||||
}
|
||||
|
||||
// findAPIGatewayStore looks for the workflow in the store
|
||||
func findAPIGatewayStore(ctx context.Context, s store.Storer, gf genericFilter) (wf *types.ApigwRoute, err error) {
|
||||
if gf.id > 0 {
|
||||
wf, err = store.LookupApigwRouteByID(ctx, s, gf.id)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wf != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, i := range gf.identifiers {
|
||||
wf, err = store.LookupApigwRouteByEndpoint(ctx, s, i)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wf != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func newAPIGatewayFromResource(res *resource.APIGateway, cfg *EncoderConfig) resourceState {
|
||||
return &apiGateway{
|
||||
cfg: mergeConfig(cfg, res.Config()),
|
||||
|
||||
res: res,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *apiGateway) Prepare(ctx context.Context, pl *payload) (err error) {
|
||||
err = n.prepareRoute(ctx, pl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return n.prepareFilters(ctx, pl)
|
||||
}
|
||||
|
||||
func (n *apiGateway) prepareRoute(ctx context.Context, pl *payload) (err error) {
|
||||
if n.cfg.IgnoreStore {
|
||||
n.res.Res.ID = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to get the original workflow
|
||||
n.gwr, err = findAPIGatewayStore(ctx, pl.s, makeGenericFilter(n.res.Identifiers()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.gwr != nil {
|
||||
n.res.Res.ID = n.gwr.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *apiGateway) prepareFilters(ctx context.Context, pl *payload) (err error) {
|
||||
if n.gwr == nil || n.gwr.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if n.cfg.IgnoreStore {
|
||||
for _, t := range n.ff {
|
||||
t.ID = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to find any related filters for this route
|
||||
tt, _, err := store.SearchApigwFilters(ctx, pl.s, types.ApigwFilterFilter{
|
||||
RouteID: n.gwr.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.ff = tt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *apiGateway) Encode(ctx context.Context, pl *payload) (err error) {
|
||||
err = n.encodeRoute(ctx, pl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return n.encodeFilters(ctx, pl)
|
||||
}
|
||||
|
||||
func (n *apiGateway) encodeRoute(ctx context.Context, pl *payload) (err error) {
|
||||
res := n.res.Res
|
||||
exists := n.gwr != nil && n.gwr.ID > 0
|
||||
|
||||
// Determine the ID
|
||||
if res.ID <= 0 && exists {
|
||||
res.ID = n.gwr.ID
|
||||
}
|
||||
if res.ID <= 0 {
|
||||
res.ID = NextID()
|
||||
}
|
||||
|
||||
// Sys users
|
||||
us, err := resolveUserstamps(ctx, pl.s, pl.state.ParentResources, n.res.Userstamps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ts := n.res.Timestamps()
|
||||
if ts != nil {
|
||||
if ts.CreatedAt != nil {
|
||||
res.CreatedAt = *ts.CreatedAt.T
|
||||
} else {
|
||||
res.CreatedAt = *now()
|
||||
}
|
||||
if ts.UpdatedAt != nil {
|
||||
res.UpdatedAt = ts.UpdatedAt.T
|
||||
}
|
||||
if ts.DeletedAt != nil {
|
||||
res.DeletedAt = ts.DeletedAt.T
|
||||
}
|
||||
}
|
||||
|
||||
res.CreatedBy = pl.invokerID
|
||||
if us != nil {
|
||||
if us.CreatedBy != nil {
|
||||
res.CreatedBy = us.CreatedBy.UserID
|
||||
}
|
||||
if us.UpdatedBy != nil {
|
||||
res.UpdatedBy = us.UpdatedBy.UserID
|
||||
}
|
||||
if us.DeletedBy != nil {
|
||||
res.DeletedBy = us.DeletedBy.UserID
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the resource skip expression
|
||||
// @todo expand available parameters; similar implementation to automation/types/record@Dict
|
||||
if skip, err := basicSkipEval(ctx, n.cfg, !exists); err != nil {
|
||||
return err
|
||||
} else if skip {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a fresh workflow
|
||||
if !exists {
|
||||
return store.CreateApigwRoute(ctx, pl.s, res)
|
||||
}
|
||||
|
||||
// Update existing workflow
|
||||
switch n.cfg.OnExisting {
|
||||
case resource.Skip:
|
||||
return nil
|
||||
|
||||
case resource.MergeLeft:
|
||||
res = mergeAPIGateways(n.gwr, res)
|
||||
|
||||
case resource.MergeRight:
|
||||
res = mergeAPIGateways(res, n.gwr)
|
||||
}
|
||||
|
||||
err = store.UpdateApigwRoute(ctx, pl.s, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.res.Res = res
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *apiGateway) encodeFilters(ctx context.Context, pl *payload) (err error) {
|
||||
exists := len(n.ff) > 0
|
||||
ff := make([]*types.ApigwFilter, 0, len(n.res.Filters))
|
||||
|
||||
for _, rf := range n.res.Filters {
|
||||
res := rf.Res
|
||||
res.Route = n.res.Res.ID
|
||||
res.ID = NextID()
|
||||
|
||||
// Sys users
|
||||
us, err := resolveUserstamps(ctx, pl.s, pl.state.ParentResources, rf.Userstamps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ts := rf.Timestamps()
|
||||
if ts != nil {
|
||||
if ts.CreatedAt != nil {
|
||||
res.CreatedAt = *ts.CreatedAt.T
|
||||
} else {
|
||||
res.CreatedAt = *now()
|
||||
}
|
||||
if ts.UpdatedAt != nil {
|
||||
res.UpdatedAt = ts.UpdatedAt.T
|
||||
}
|
||||
if ts.DeletedAt != nil {
|
||||
res.DeletedAt = ts.DeletedAt.T
|
||||
}
|
||||
}
|
||||
res.CreatedBy = pl.invokerID
|
||||
if us != nil {
|
||||
if us.CreatedBy != nil {
|
||||
res.CreatedBy = us.CreatedBy.UserID
|
||||
}
|
||||
if us.UpdatedBy != nil {
|
||||
res.UpdatedBy = us.UpdatedBy.UserID
|
||||
}
|
||||
if us.DeletedBy != nil {
|
||||
res.DeletedBy = us.DeletedBy.UserID
|
||||
}
|
||||
}
|
||||
|
||||
ff = append(ff, res)
|
||||
}
|
||||
|
||||
// Create a fresh workflow
|
||||
if !exists {
|
||||
return store.CreateApigwFilter(ctx, pl.s, ff...)
|
||||
}
|
||||
|
||||
// If these filters already exist and we wish to modify them,
|
||||
// remove the old ones and create new ones
|
||||
switch n.cfg.OnExisting {
|
||||
case resource.Skip,
|
||||
resource.MergeLeft:
|
||||
return nil
|
||||
}
|
||||
|
||||
err = store.DeleteApigwFilter(ctx, pl.s, n.ff...)
|
||||
return store.CreateApigwFilter(ctx, pl.s, ff...)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func newAPIGateway(gwr *types.ApigwRoute, ff types.ApigwFilterSet, ux *userIndex) *apiGateway {
|
||||
return &apiGateway{
|
||||
gwr: gwr,
|
||||
ff: ff,
|
||||
|
||||
ux: ux,
|
||||
}
|
||||
}
|
||||
|
||||
func (awf *apiGateway) MarshalEnvoy() ([]resource.Interface, error) {
|
||||
rs := resource.NewAPIGateway(awf.gwr)
|
||||
syncUserStamps(rs.Userstamps(), awf.ux)
|
||||
|
||||
for _, f := range awf.ff {
|
||||
rt := rs.AddGatewayFilter(f)
|
||||
syncUserStamps(rt.Userstamps(), awf.ux)
|
||||
}
|
||||
|
||||
return envoy.CollectNodes(
|
||||
rs,
|
||||
)
|
||||
}
|
||||
@@ -17,8 +17,9 @@ type (
|
||||
res *resource.ComposeModule
|
||||
mod *types.Module
|
||||
|
||||
relNS *types.Namespace
|
||||
recFields map[string]uint64
|
||||
relNS *types.Namespace
|
||||
recFields map[string]uint64
|
||||
userFields map[string]uint64
|
||||
}
|
||||
)
|
||||
|
||||
@@ -203,3 +204,7 @@ func findComposeModuleFieldsStore(ctx context.Context, s store.Storer, mod *type
|
||||
func composeModuleErrUnresolvedRecordField(ii resource.Identifiers) error {
|
||||
return fmt.Errorf("record module field unresolved %v", ii.StringSlice())
|
||||
}
|
||||
|
||||
func composeModuleErrUnresolvedUserField(ii resource.Identifiers) error {
|
||||
return fmt.Errorf("user module field unresolved %v", ii.StringSlice())
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func NewComposeModuleFromResource(res *resource.ComposeModule, cfg *EncoderConfig) resourceState {
|
||||
@@ -15,7 +16,8 @@ func NewComposeModuleFromResource(res *resource.ComposeModule, cfg *EncoderConfi
|
||||
|
||||
res: res,
|
||||
|
||||
recFields: make(map[string]uint64),
|
||||
recFields: make(map[string]uint64),
|
||||
userFields: make(map[string]uint64),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +59,25 @@ func (n *composeModule) Prepare(ctx context.Context, pl *payload) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get related user field roles
|
||||
for _, refRole := range n.res.RefRoles {
|
||||
var rl *systemTypes.Role
|
||||
rl, err = findRoleStore(ctx, pl.s, makeGenericFilter(refRole.Identifiers))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rl == nil {
|
||||
rl = resource.FindRole(pl.state.ParentResources, refRole.Identifiers)
|
||||
}
|
||||
if rl == nil {
|
||||
return composeModuleErrUnresolvedUserField(refRole.Identifiers)
|
||||
}
|
||||
|
||||
for i := range refRole.Identifiers {
|
||||
n.userFields[i] = rl.ID
|
||||
}
|
||||
}
|
||||
|
||||
// Can't do anything else, since the NS doesn't yet exist
|
||||
if n.cfg.IgnoreStore || n.relNS.ID <= 0 {
|
||||
return nil
|
||||
@@ -164,6 +185,35 @@ func (n *composeModule) Encode(ctx context.Context, pl *payload) (err error) {
|
||||
f.Options["moduleID"] = strconv.FormatUint(modID, 10)
|
||||
delete(f.Options, "module")
|
||||
}
|
||||
|
||||
if f.Kind == "User" {
|
||||
roles := resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["roles"])
|
||||
if len(roles) == 0 {
|
||||
roles = resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["role"])
|
||||
}
|
||||
if len(roles) == 0 {
|
||||
roles = resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["roleID"])
|
||||
}
|
||||
|
||||
var out []string
|
||||
for _, r := range roles {
|
||||
roleID := n.userFields[r]
|
||||
if roleID <= 0 {
|
||||
ii := resource.MakeIdentifiers(r)
|
||||
role := resource.FindRole(pl.state.ParentResources, ii)
|
||||
if role == nil || role.ID == 0 {
|
||||
return composeModuleErrUnresolvedUserField(ii)
|
||||
}
|
||||
roleID = role.ID
|
||||
}
|
||||
|
||||
out = append(out, strconv.FormatUint(roleID, 10))
|
||||
}
|
||||
|
||||
f.Options["roles"] = out
|
||||
delete(f.Options, "role")
|
||||
delete(f.Options, "roleID")
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the resource skip expression
|
||||
|
||||
@@ -329,6 +329,18 @@ func (n *composePage) Encode(ctx context.Context, pl *payload) (err error) {
|
||||
delete(mops, "module")
|
||||
|
||||
}
|
||||
|
||||
case "Comment":
|
||||
id := ss(b.Options, "module", "moduleID")
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
mID := getModID(id)
|
||||
if mID <= 0 {
|
||||
return resource.ComposeModuleErrUnresolved(resource.MakeIdentifiers(id))
|
||||
}
|
||||
b.Options["moduleID"] = strconv.FormatUint(mID, 10)
|
||||
delete(b.Options, "module")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,13 @@ type (
|
||||
composeChart []*composeChartFilter
|
||||
|
||||
// System stuff
|
||||
roles []*roleFilter
|
||||
users []*userFilter
|
||||
templates []*templateFilter
|
||||
applications []*applicationFilter
|
||||
roles []*roleFilter
|
||||
users []*userFilter
|
||||
templates []*templateFilter
|
||||
applications []*applicationFilter
|
||||
apiGwRoutes []*apiGwRouteFilter
|
||||
reports []*reportFilter
|
||||
|
||||
settings []*settingFilter
|
||||
rbac []*rbacFilter
|
||||
resourceTranslations []*resourceTranslationFilter
|
||||
@@ -146,6 +149,8 @@ func (d *decoder) Decode(ctx context.Context, s store.Storer, f *DecodeFilter) (
|
||||
system.decodeUsers(ctx, s, f.users),
|
||||
system.decodeTemplates(ctx, s, f.templates),
|
||||
system.decodeApplications(ctx, s, f.applications),
|
||||
system.decodeAPIGWRoutes(ctx, s, f.apiGwRoutes),
|
||||
system.decodeReports(ctx, s, f.reports),
|
||||
system.decodeSettings(ctx, s, f.settings),
|
||||
system.decodeResourceTranslation(ctx, s, f.resourceTranslations),
|
||||
|
||||
|
||||
@@ -146,6 +146,10 @@ func (se *storeEncoder) Prepare(ctx context.Context, ee ...*envoy.ResourceState)
|
||||
err = f(NewRoleFromResource(res, se.cfg), ers)
|
||||
case *resource.Application:
|
||||
err = f(NewApplicationFromResource(res, se.cfg), ers)
|
||||
case *resource.APIGateway:
|
||||
err = f(newAPIGatewayFromResource(res, se.cfg), ers)
|
||||
case *resource.Report:
|
||||
err = f(newReportFromResource(res, se.cfg), ers)
|
||||
case *resource.Setting:
|
||||
err = f(NewSettingFromResource(res, se.cfg), ers)
|
||||
case *resource.RbacRule:
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
report struct {
|
||||
cfg *EncoderConfig
|
||||
|
||||
res *resource.Report
|
||||
rp *types.Report
|
||||
ss types.ReportDataSourceSet
|
||||
pp types.ReportProjectionSet
|
||||
|
||||
ux *userIndex
|
||||
}
|
||||
reportSet []*report
|
||||
|
||||
reportSource struct {
|
||||
cfg *EncoderConfig
|
||||
|
||||
res *resource.ReportSource
|
||||
tr *types.ReportDataSource
|
||||
}
|
||||
reportSourceSet []*reportSource
|
||||
)
|
||||
|
||||
// mergeReports merges b into a, prioritising a
|
||||
func mergeReports(a, b *types.Report) *types.Report {
|
||||
c := a
|
||||
|
||||
if c.Handle == "" {
|
||||
c.Handle = b.Handle
|
||||
}
|
||||
if c.Meta == nil {
|
||||
c.Meta = b.Meta
|
||||
}
|
||||
if c.Sources == nil {
|
||||
c.Sources = b.Sources
|
||||
}
|
||||
if c.Projections == nil {
|
||||
c.Projections = b.Projections
|
||||
}
|
||||
|
||||
if c.OwnedBy == 0 {
|
||||
c.OwnedBy = b.OwnedBy
|
||||
}
|
||||
if c.CreatedBy == 0 {
|
||||
c.CreatedBy = b.CreatedBy
|
||||
}
|
||||
if c.UpdatedBy == 0 {
|
||||
c.UpdatedBy = b.UpdatedBy
|
||||
}
|
||||
if c.DeletedBy == 0 {
|
||||
c.DeletedBy = b.DeletedBy
|
||||
}
|
||||
|
||||
if c.CreatedAt.IsZero() {
|
||||
c.CreatedAt = b.CreatedAt
|
||||
}
|
||||
|
||||
if c.UpdatedAt == nil {
|
||||
c.UpdatedAt = b.UpdatedAt
|
||||
}
|
||||
|
||||
if c.DeletedAt == nil {
|
||||
c.DeletedAt = b.DeletedAt
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// findReport looks for the report in the resources & the store
|
||||
//
|
||||
// Provided resources are prioritized.
|
||||
func findReport(ctx context.Context, s store.Storer, rr resource.InterfaceSet, ii resource.Identifiers) (wf *types.Report, err error) {
|
||||
wf = resource.FindReport(rr, ii)
|
||||
if wf != nil {
|
||||
return wf, nil
|
||||
}
|
||||
|
||||
return findReportStore(ctx, s, makeGenericFilter(ii))
|
||||
}
|
||||
|
||||
// findReportStore looks for the report in the store
|
||||
func findReportStore(ctx context.Context, s store.Storer, gf genericFilter) (wf *types.Report, err error) {
|
||||
if gf.id > 0 {
|
||||
wf, err = store.LookupReportByID(ctx, s, gf.id)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wf != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, i := range gf.identifiers {
|
||||
wf, err = store.LookupReportByHandle(ctx, s, i)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wf != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func newReportFromResource(res *resource.Report, cfg *EncoderConfig) resourceState {
|
||||
return &report{
|
||||
cfg: mergeConfig(cfg, res.Config()),
|
||||
|
||||
res: res,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *report) Prepare(ctx context.Context, pl *payload) (err error) {
|
||||
if n.cfg.IgnoreStore {
|
||||
n.res.Res.ID = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to get the original report
|
||||
n.rp, err = findReportStore(ctx, pl.s, makeGenericFilter(n.res.Identifiers()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.rp != nil {
|
||||
n.res.Res.ID = n.rp.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *report) Encode(ctx context.Context, pl *payload) (err error) {
|
||||
res := n.res.Res
|
||||
exists := n.rp != nil && n.rp.ID > 0
|
||||
|
||||
// Determine the ID
|
||||
if res.ID <= 0 && exists {
|
||||
res.ID = n.rp.ID
|
||||
}
|
||||
if res.ID <= 0 {
|
||||
res.ID = NextID()
|
||||
}
|
||||
|
||||
// Sys users
|
||||
us, err := resolveUserstamps(ctx, pl.s, pl.state.ParentResources, n.res.Userstamps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ts := n.res.Timestamps()
|
||||
if ts != nil {
|
||||
if ts.CreatedAt != nil {
|
||||
res.CreatedAt = *ts.CreatedAt.T
|
||||
} else {
|
||||
res.CreatedAt = *now()
|
||||
}
|
||||
if ts.UpdatedAt != nil {
|
||||
res.UpdatedAt = ts.UpdatedAt.T
|
||||
}
|
||||
if ts.DeletedAt != nil {
|
||||
res.DeletedAt = ts.DeletedAt.T
|
||||
}
|
||||
}
|
||||
|
||||
res.CreatedBy = pl.invokerID
|
||||
if us != nil {
|
||||
if us.OwnedBy != nil {
|
||||
res.OwnedBy = us.OwnedBy.UserID
|
||||
}
|
||||
if us.CreatedBy != nil {
|
||||
res.CreatedBy = us.CreatedBy.UserID
|
||||
}
|
||||
if us.UpdatedBy != nil {
|
||||
res.UpdatedBy = us.UpdatedBy.UserID
|
||||
}
|
||||
if us.DeletedBy != nil {
|
||||
res.DeletedBy = us.DeletedBy.UserID
|
||||
}
|
||||
}
|
||||
|
||||
res.Sources = make(types.ReportDataSourceSet, 0, 10)
|
||||
for _, rp := range n.res.Sources {
|
||||
res.Sources = append(res.Sources, rp.Res)
|
||||
}
|
||||
|
||||
res.Projections = make(types.ReportProjectionSet, 0, 10)
|
||||
for _, rp := range n.res.Projections {
|
||||
res.Projections = append(res.Projections, rp.Res)
|
||||
}
|
||||
|
||||
// Evaluate the resource skip expression
|
||||
// @todo expand available parameters; similar implementation to automation/types/record@Dict
|
||||
if skip, err := basicSkipEval(ctx, n.cfg, !exists); err != nil {
|
||||
return err
|
||||
} else if skip {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a fresh report
|
||||
if !exists {
|
||||
return store.CreateReport(ctx, pl.s, res)
|
||||
}
|
||||
|
||||
// Update existing report
|
||||
switch n.cfg.OnExisting {
|
||||
case resource.Skip:
|
||||
return nil
|
||||
|
||||
case resource.MergeLeft:
|
||||
res = mergeReports(n.rp, res)
|
||||
|
||||
case resource.MergeRight:
|
||||
res = mergeReports(res, n.rp)
|
||||
}
|
||||
|
||||
err = store.UpdateReport(ctx, pl.s, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.res.Res = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func newReport(wf *types.Report, ux *userIndex) *report {
|
||||
return &report{
|
||||
rp: wf,
|
||||
ss: wf.Sources,
|
||||
pp: wf.Projections,
|
||||
|
||||
ux: ux,
|
||||
}
|
||||
}
|
||||
|
||||
func (awf *report) MarshalEnvoy() ([]resource.Interface, error) {
|
||||
rs := resource.NewReport(awf.rp)
|
||||
syncUserStamps(rs.Userstamps(), awf.ux)
|
||||
|
||||
for _, s := range awf.ss {
|
||||
rs.AddReportSource(s)
|
||||
}
|
||||
|
||||
for _, p := range awf.pp {
|
||||
rs.AddReportProjection(p)
|
||||
}
|
||||
|
||||
return envoy.CollectNodes(
|
||||
rs,
|
||||
)
|
||||
}
|
||||
@@ -209,6 +209,15 @@ func (n *resourceTranslation) makeResourceTranslation(pl *payload) (string, erro
|
||||
|
||||
return composeTypes.ModuleFieldResourceTranslation(p0ID, p1ID, p2ID), nil
|
||||
|
||||
case types.ReportResourceType:
|
||||
p1 := resource.FindReport(pl.state.ParentResources, n.refLocaleRes.Identifiers)
|
||||
if p1 == nil {
|
||||
return "", resource.ReportErrUnresolved(n.refLocaleRes.Identifiers)
|
||||
}
|
||||
p1ID = p1.ID
|
||||
|
||||
return types.ReportResourceTranslation(p1ID), nil
|
||||
|
||||
default:
|
||||
// @todo if we wish to support res. trans. for external stuff, this needs to pass through.
|
||||
// this also requires some tweaks in the path ID thing.
|
||||
|
||||
@@ -18,6 +18,8 @@ type (
|
||||
userFilter types.UserFilter
|
||||
templateFilter types.TemplateFilter
|
||||
applicationFilter types.ApplicationFilter
|
||||
apiGwRouteFilter types.ApigwRouteFilter
|
||||
reportFilter types.ReportFilter
|
||||
settingFilter types.SettingsFilter
|
||||
rbacFilter struct {
|
||||
rbac.RuleFilter
|
||||
@@ -175,6 +177,104 @@ func (d *systemDecoder) decodeTemplates(ctx context.Context, s store.Storer, ff
|
||||
}
|
||||
}
|
||||
|
||||
func (d *systemDecoder) decodeAPIGWRoutes(ctx context.Context, s store.Storer, ff []*apiGwRouteFilter) *auxRsp {
|
||||
mm := make([]envoy.Marshaller, 0, 100)
|
||||
if ff == nil {
|
||||
return &auxRsp{
|
||||
mm: mm,
|
||||
}
|
||||
}
|
||||
|
||||
var nn types.ApigwRouteSet
|
||||
var fn types.ApigwRouteFilter
|
||||
var err error
|
||||
|
||||
for _, f := range ff {
|
||||
aux := *f
|
||||
|
||||
if aux.Limit == 0 {
|
||||
aux.Limit = 1000
|
||||
}
|
||||
|
||||
for {
|
||||
nn, fn, err = s.SearchApigwRoutes(ctx, types.ApigwRouteFilter(aux))
|
||||
if err != nil {
|
||||
return &auxRsp{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// filters
|
||||
for _, n := range nn {
|
||||
gwf, _, err := s.SearchApigwFilters(ctx, types.ApigwFilterFilter{RouteID: n.ID})
|
||||
if err != nil {
|
||||
return &auxRsp{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
mm = append(mm, newAPIGateway(n, gwf, d.ux))
|
||||
d.resourceID = append(d.resourceID, n.ID)
|
||||
}
|
||||
|
||||
if fn.NextPage != nil {
|
||||
aux.PageCursor = fn.NextPage
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &auxRsp{
|
||||
mm: mm,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *systemDecoder) decodeReports(ctx context.Context, s store.Storer, ff []*reportFilter) *auxRsp {
|
||||
mm := make([]envoy.Marshaller, 0, 100)
|
||||
if ff == nil {
|
||||
return &auxRsp{
|
||||
mm: mm,
|
||||
}
|
||||
}
|
||||
|
||||
var nn types.ReportSet
|
||||
var fn types.ReportFilter
|
||||
var err error
|
||||
|
||||
for _, f := range ff {
|
||||
aux := *f
|
||||
|
||||
if aux.Limit == 0 {
|
||||
aux.Limit = 1000
|
||||
}
|
||||
|
||||
for {
|
||||
nn, fn, err = s.SearchReports(ctx, types.ReportFilter(aux))
|
||||
if err != nil {
|
||||
return &auxRsp{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range nn {
|
||||
mm = append(mm, newReport(n, d.ux))
|
||||
d.resourceID = append(d.resourceID, n.ID)
|
||||
}
|
||||
|
||||
if fn.NextPage != nil {
|
||||
aux.PageCursor = fn.NextPage
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &auxRsp{
|
||||
mm: mm,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *systemDecoder) decodeApplications(ctx context.Context, s store.Storer, ff []*applicationFilter) *auxRsp {
|
||||
mm := make([]envoy.Marshaller, 0, 100)
|
||||
if ff == nil {
|
||||
@@ -467,6 +567,21 @@ func (df *DecodeFilter) systemFromResource(rr ...string) *DecodeFilter {
|
||||
TemplateID: []uint64{templateID},
|
||||
})
|
||||
}
|
||||
case "system:apigw-route":
|
||||
df = df.APIGWRoutes(&types.ApigwRouteFilter{
|
||||
Route: id,
|
||||
})
|
||||
case "system:report":
|
||||
df = df.Reports(&types.ReportFilter{
|
||||
Handle: id,
|
||||
})
|
||||
reportID, err := cast.ToUint64E(id)
|
||||
if err == nil && reportID > 0 {
|
||||
df = df.Reports(&types.ReportFilter{
|
||||
ReportID: []uint64{reportID},
|
||||
})
|
||||
}
|
||||
|
||||
case "system:application":
|
||||
df = df.Applications(&types.ApplicationFilter{
|
||||
Query: id,
|
||||
@@ -554,6 +669,22 @@ func (df *DecodeFilter) Templates(f *types.TemplateFilter) *DecodeFilter {
|
||||
return df
|
||||
}
|
||||
|
||||
func (df *DecodeFilter) APIGWRoutes(f *types.ApigwRouteFilter) *DecodeFilter {
|
||||
if df.apiGwRoutes == nil {
|
||||
df.apiGwRoutes = make([]*apiGwRouteFilter, 0, 1)
|
||||
}
|
||||
df.apiGwRoutes = append(df.apiGwRoutes, (*apiGwRouteFilter)(f))
|
||||
return df
|
||||
}
|
||||
|
||||
func (df *DecodeFilter) Reports(f *types.ReportFilter) *DecodeFilter {
|
||||
if df.reports == nil {
|
||||
df.reports = make([]*reportFilter, 0, 1)
|
||||
}
|
||||
df.reports = append(df.reports, (*reportFilter)(f))
|
||||
return df
|
||||
}
|
||||
|
||||
// Applications adds a new ApplicationFilter
|
||||
func (df *DecodeFilter) Applications(f *types.ApplicationFilter) *DecodeFilter {
|
||||
if df.applications == nil {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
apiGateway struct {
|
||||
res *types.ApigwRoute
|
||||
filters apiGwFilterSet
|
||||
|
||||
ts *resource.Timestamps
|
||||
us *resource.Userstamps
|
||||
|
||||
envoyConfig *resource.EnvoyConfig
|
||||
encoderConfig *EncoderConfig
|
||||
|
||||
rbac rbacRuleSet
|
||||
}
|
||||
apiGatewaySet []*apiGateway
|
||||
|
||||
apiGwFilter struct {
|
||||
res *types.ApigwFilter
|
||||
|
||||
ts *resource.Timestamps
|
||||
us *resource.Userstamps
|
||||
|
||||
envoyConfig *resource.EnvoyConfig
|
||||
encoderConfig *EncoderConfig
|
||||
}
|
||||
apiGwFilterSet []*apiGwFilter
|
||||
)
|
||||
|
||||
func (nn apiGatewaySet) configureEncoder(cfg *EncoderConfig) {
|
||||
for _, n := range nn {
|
||||
n.encoderConfig = cfg
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func apiGatewayFromResource(r *resource.APIGateway, cfg *EncoderConfig) *apiGateway {
|
||||
ff := make(apiGwFilterSet, len(r.Filters))
|
||||
for i, t := range r.Filters {
|
||||
ff[i] = &apiGwFilter{
|
||||
res: t.Res,
|
||||
encoderConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
return &apiGateway{
|
||||
res: r.Res,
|
||||
filters: ff,
|
||||
|
||||
encoderConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *apiGateway) Prepare(ctx context.Context, state *envoy.ResourceState) (err error) {
|
||||
wf, ok := state.Res.(*resource.APIGateway)
|
||||
if !ok {
|
||||
return encoderErrInvalidResource(systemTypes.ApigwRouteResourceType, state.Res.ResourceType())
|
||||
}
|
||||
|
||||
n.res = wf.Res
|
||||
n.us = wf.Userstamps()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *apiGateway) Encode(ctx context.Context, doc *Document, state *envoy.ResourceState) (err error) {
|
||||
if n.res.ID <= 0 {
|
||||
n.res.ID = nextID()
|
||||
}
|
||||
|
||||
n.ts, err = resource.MakeTimestampsCUDA(&n.res.CreatedAt, n.res.UpdatedAt, n.res.DeletedAt, nil).
|
||||
Model(n.encoderConfig.TimeLayout, n.encoderConfig.Timezone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.us, err = resolveUserstamps(state.ParentResources, n.us)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// @todo skip eval?
|
||||
|
||||
doc.addApiGateway(n)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *apiGateway) MarshalYAML() (interface{}, error) {
|
||||
var err error
|
||||
|
||||
nn, err := makeMap(
|
||||
"endpoint", g.res.Endpoint,
|
||||
"method", g.res.Method,
|
||||
"enabled", g.res.Enabled,
|
||||
"group", g.res.Group,
|
||||
"meta", g.res.Meta,
|
||||
|
||||
"filters", g.filters,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nn, err = encodeTimestamps(nn, g.ts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nn, err = encodeUserstamps(nn, g.us)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (f *apiGwFilter) MarshalYAML() (interface{}, error) {
|
||||
var err error
|
||||
|
||||
nn, err := makeMap(
|
||||
"weight", f.res.Weight,
|
||||
"ref", f.res.Ref,
|
||||
"kind", f.res.Kind,
|
||||
"params", f.res.Params,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nn, err = encodeTimestamps(nn, f.ts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nn, err = encodeUserstamps(nn, f.us)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/pkg/y7s"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func (wset *apiGatewaySet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return y7s.Each(n, func(k, v *yaml.Node) (err error) {
|
||||
var (
|
||||
wrap = &apiGateway{}
|
||||
)
|
||||
|
||||
if v == nil {
|
||||
return y7s.NodeErr(n, "malformed api gateway definition")
|
||||
}
|
||||
|
||||
if err = v.Decode(&wrap); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*wset = append(*wset, wrap)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap *apiGateway) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
if wrap.res == nil {
|
||||
wrap.rbac = make(rbacRuleSet, 0, 10)
|
||||
wrap.res = &types.ApigwRoute{}
|
||||
}
|
||||
|
||||
if wrap.rbac, err = decodeRbac(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.envoyConfig, err = decodeEnvoyConfig(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.ts, err = decodeTimestamps(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.us, err = decodeUserstamps(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch strings.ToLower(k.Value) {
|
||||
case "endpoint":
|
||||
return y7s.DecodeScalar(v, "api gw endpoint", &wrap.res.Endpoint)
|
||||
case "method":
|
||||
return y7s.DecodeScalar(v, "api gw method", &wrap.res.Method)
|
||||
case "enabled":
|
||||
return y7s.DecodeScalar(v, "api gw enabled", &wrap.res.Enabled)
|
||||
case "group":
|
||||
return y7s.DecodeScalar(v, "api gw group", &wrap.res.Group)
|
||||
case "meta":
|
||||
aux := &types.ApigwRouteMeta{}
|
||||
err = v.Decode(&aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wrap.res.Meta = *aux
|
||||
return nil
|
||||
|
||||
case "filters":
|
||||
wrap.filters = make(apiGwFilterSet, 0, 10)
|
||||
|
||||
err = v.Decode(&wrap.filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap *apiGwFilter) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
if wrap.res == nil {
|
||||
wrap.res = &types.ApigwFilter{}
|
||||
}
|
||||
|
||||
if wrap.envoyConfig, err = decodeEnvoyConfig(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.ts, err = decodeTimestamps(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.us, err = decodeUserstamps(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch strings.ToLower(k.Value) {
|
||||
|
||||
case "weight":
|
||||
return y7s.DecodeScalar(v, "route filter weight", &wrap.res.Weight)
|
||||
|
||||
case "ref":
|
||||
return y7s.DecodeScalar(v, "route filter ref", &wrap.res.Ref)
|
||||
|
||||
case "kind":
|
||||
return y7s.DecodeScalar(v, "route filter kind", &wrap.res.Kind)
|
||||
|
||||
case "params":
|
||||
return v.Decode(&wrap.res.Params)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wset apiGatewaySet) MarshalEnvoy() ([]resource.Interface, error) {
|
||||
nn := make([]resource.Interface, 0, len(wset)*2)
|
||||
|
||||
for _, res := range wset {
|
||||
if tmp, err := res.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (wrap apiGateway) MarshalEnvoy() ([]resource.Interface, error) {
|
||||
rs := resource.NewAPIGateway(wrap.res)
|
||||
rs.SetTimestamps(wrap.ts)
|
||||
rs.SetUserstamps(wrap.us)
|
||||
rs.SetConfig(wrap.envoyConfig)
|
||||
|
||||
for _, f := range wrap.filters {
|
||||
trs := rs.AddGatewayFilter(f.res)
|
||||
trs.SetTimestamps(f.ts)
|
||||
trs.SetUserstamps(f.us)
|
||||
}
|
||||
|
||||
return envoy.CollectNodes(
|
||||
rs,
|
||||
wrap.rbac.bindResource(rs),
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package yaml
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -42,7 +43,8 @@ type (
|
||||
cfg *EncoderConfig
|
||||
expr composeModuleFieldExpr
|
||||
|
||||
relMod *types.Module
|
||||
relMod *types.Module
|
||||
relRoles systemTypes.RoleSet
|
||||
|
||||
rbac rbacRuleSet
|
||||
locale resourceTranslationSet
|
||||
|
||||
@@ -3,6 +3,7 @@ package yaml
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
@@ -38,7 +39,8 @@ func (n *composeModule) Prepare(ctx context.Context, state *envoy.ResourceState)
|
||||
expr: composeModuleFieldExpr(f.Expressions),
|
||||
}
|
||||
|
||||
if f.Kind == "Record" {
|
||||
switch f.Kind {
|
||||
case "Record":
|
||||
refMod := f.Options.String("module")
|
||||
if refMod == "" {
|
||||
refMod = f.Options.String("moduleID")
|
||||
@@ -47,6 +49,23 @@ func (n *composeModule) Prepare(ctx context.Context, state *envoy.ResourceState)
|
||||
if cmf.relMod == nil {
|
||||
return resource.ComposeModuleErrUnresolved(resource.MakeIdentifiers(refMod))
|
||||
}
|
||||
|
||||
case "User":
|
||||
refRoles := resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["roles"])
|
||||
if len(refRoles) == 0 {
|
||||
refRoles = resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["role"])
|
||||
}
|
||||
if len(refRoles) == 0 {
|
||||
refRoles = resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["roleID"])
|
||||
}
|
||||
|
||||
for _, ref := range refRoles {
|
||||
aux := resource.FindRole(state.ParentResources, resource.MakeIdentifiers(ref))
|
||||
if aux == nil {
|
||||
return resource.RoleErrUnresolved(resource.MakeIdentifiers(ref))
|
||||
}
|
||||
cmf.relRoles = append(cmf.relRoles, aux)
|
||||
}
|
||||
}
|
||||
|
||||
n.fields = append(n.fields, cmf)
|
||||
@@ -136,7 +155,8 @@ func (c *composeModule) MarshalYAML() (interface{}, error) {
|
||||
func (c *composeModuleField) MarshalYAML() (interface{}, error) {
|
||||
|
||||
auxOpt := c.res.Options
|
||||
if c.res.Kind == "Record" {
|
||||
switch c.res.Kind {
|
||||
case "Record":
|
||||
ref := c.relMod.Handle
|
||||
if ref == "" {
|
||||
ref = c.relMod.Name
|
||||
@@ -144,6 +164,16 @@ func (c *composeModuleField) MarshalYAML() (interface{}, error) {
|
||||
|
||||
auxOpt["module"] = ref
|
||||
delete(auxOpt, "moduleID")
|
||||
|
||||
case "User":
|
||||
aux := make([]string, 0, len(c.relRoles))
|
||||
for _, r := range c.relRoles {
|
||||
aux = append(aux, firstOkString(r.Handle, strconv.FormatUint(r.ID, 10)))
|
||||
}
|
||||
|
||||
auxOpt["roles"] = aux
|
||||
delete(auxOpt, "role")
|
||||
delete(auxOpt, "roleID")
|
||||
}
|
||||
|
||||
if _, has := auxOpt["multiDelimiter"]; has {
|
||||
|
||||
@@ -228,6 +228,11 @@ func (c *composePageBlock) MarshalYAML() (interface{}, error) {
|
||||
}
|
||||
break
|
||||
|
||||
case "Comment":
|
||||
opt["moduleID"] = c.refMod[0]
|
||||
delete(opt, "module")
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return makeMap(
|
||||
|
||||
@@ -19,9 +19,13 @@ type (
|
||||
users userSet
|
||||
templates templateSet
|
||||
applications applicationSet
|
||||
settings settingSet
|
||||
rbac rbacRuleSet
|
||||
locale resourceTranslationSet
|
||||
|
||||
apiGateway apiGatewaySet
|
||||
reports reportSet
|
||||
|
||||
settings settingSet
|
||||
rbac rbacRuleSet
|
||||
locale resourceTranslationSet
|
||||
|
||||
cfg *EncoderConfig
|
||||
}
|
||||
@@ -55,6 +59,12 @@ func (doc *Document) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
case "templates":
|
||||
return v.Decode(&doc.templates)
|
||||
|
||||
case "apigateway", "apigw":
|
||||
return v.Decode(&doc.apiGateway)
|
||||
|
||||
case "reports":
|
||||
return v.Decode(&doc.reports)
|
||||
|
||||
case "applications":
|
||||
return v.Decode(&doc.applications)
|
||||
|
||||
@@ -120,6 +130,28 @@ func (doc *Document) MarshalYAML() (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if doc.reports != nil && len(doc.reports) > 0 {
|
||||
doc.reports.configureEncoder(doc.cfg)
|
||||
|
||||
dn, err = encodeResource(dn, "reports", doc.reports, doc.cfg.MappedOutput, "handle")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if doc.apiGateway != nil && len(doc.apiGateway) > 0 {
|
||||
doc.apiGateway.configureEncoder(doc.cfg)
|
||||
|
||||
// API GW don't support map representation
|
||||
// @todo use path+proto?
|
||||
dn, err = addMap(dn,
|
||||
"apigateway", doc.apiGateway,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if doc.applications != nil && len(doc.applications) > 0 {
|
||||
doc.applications.configureEncoder(doc.cfg)
|
||||
|
||||
@@ -186,6 +218,12 @@ func (doc *Document) Decode(ctx context.Context) ([]resource.Interface, error) {
|
||||
if doc.applications != nil {
|
||||
mm = append(mm, doc.applications)
|
||||
}
|
||||
if doc.reports != nil {
|
||||
mm = append(mm, doc.reports)
|
||||
}
|
||||
if doc.apiGateway != nil {
|
||||
mm = append(mm, doc.apiGateway)
|
||||
}
|
||||
if doc.settings != nil {
|
||||
for _, s := range doc.settings {
|
||||
mm = append(mm, s)
|
||||
@@ -275,6 +313,14 @@ func (doc *Document) addAutomationWorkflow(m *automationWorkflow) {
|
||||
doc.automation.Workflows = append(doc.automation.Workflows, m)
|
||||
}
|
||||
|
||||
func (doc *Document) addApiGateway(a *apiGateway) {
|
||||
doc.apiGateway = append(doc.apiGateway, a)
|
||||
}
|
||||
|
||||
func (doc *Document) addReport(a *report) {
|
||||
doc.reports = append(doc.reports, a)
|
||||
}
|
||||
|
||||
func (doc *Document) addRole(r *role) {
|
||||
if doc.roles == nil {
|
||||
doc.roles = make(roleSet, 0, 20)
|
||||
|
||||
@@ -98,6 +98,10 @@ func NewYamlEncoder(cfg *EncoderConfig) envoy.PrepareEncodeStreamer {
|
||||
// It initializes and prepares the resource state for each provided resource
|
||||
func (ye *yamlEncoder) Prepare(ctx context.Context, ee ...*envoy.ResourceState) (err error) {
|
||||
f := func(rs resourceState, es *envoy.ResourceState) error {
|
||||
if rs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = rs.Prepare(ctx, es)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -135,6 +139,10 @@ func (ye *yamlEncoder) Prepare(ctx context.Context, ee ...*envoy.ResourceState)
|
||||
err = f(templateFromResource(res, ye.cfg), e)
|
||||
case *resource.Application:
|
||||
err = f(applicationFromResource(res, ye.cfg), e)
|
||||
case *resource.APIGateway:
|
||||
err = f(apiGatewayFromResource(res, ye.cfg), e)
|
||||
case *resource.Report:
|
||||
err = f(reportFromResource(res, ye.cfg), e)
|
||||
case *resource.Setting:
|
||||
err = f(settingFromResource(res, ye.cfg), e)
|
||||
case *resource.RbacRule:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
report struct {
|
||||
res *types.Report
|
||||
sources reportSourceSet
|
||||
projections reportProjectionSet
|
||||
|
||||
ts *resource.Timestamps
|
||||
us *resource.Userstamps
|
||||
|
||||
envoyConfig *resource.EnvoyConfig
|
||||
encoderConfig *EncoderConfig
|
||||
|
||||
rbac rbacRuleSet
|
||||
locale resourceTranslationSet
|
||||
}
|
||||
reportSet []*report
|
||||
|
||||
reportSource struct {
|
||||
res *types.ReportDataSource
|
||||
|
||||
envoyConfig *resource.EnvoyConfig
|
||||
encoderConfig *EncoderConfig
|
||||
}
|
||||
reportSourceSet []*reportSource
|
||||
|
||||
reportProjection struct {
|
||||
res *types.ReportProjection
|
||||
|
||||
envoyConfig *resource.EnvoyConfig
|
||||
encoderConfig *EncoderConfig
|
||||
|
||||
locale resourceTranslationSet
|
||||
}
|
||||
reportProjectionSet []*reportProjection
|
||||
)
|
||||
|
||||
func (nn reportSet) configureEncoder(cfg *EncoderConfig) {
|
||||
for _, n := range nn {
|
||||
n.encoderConfig = cfg
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
automationTypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
)
|
||||
|
||||
func reportFromResource(r *resource.Report, cfg *EncoderConfig) *report {
|
||||
ss := make(reportSourceSet, len(r.Sources))
|
||||
for i, s := range r.Sources {
|
||||
ss[i] = &reportSource{
|
||||
res: s.Res,
|
||||
encoderConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
pp := make(reportProjectionSet, len(r.Projections))
|
||||
for i, p := range r.Projections {
|
||||
pp[i] = &reportProjection{
|
||||
res: p.Res,
|
||||
encoderConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
return &report{
|
||||
res: r.Res,
|
||||
sources: ss,
|
||||
projections: pp,
|
||||
|
||||
encoderConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *report) Prepare(ctx context.Context, state *envoy.ResourceState) (err error) {
|
||||
wf, ok := state.Res.(*resource.Report)
|
||||
if !ok {
|
||||
return encoderErrInvalidResource(automationTypes.WorkflowResourceType, state.Res.ResourceType())
|
||||
}
|
||||
|
||||
n.res = wf.Res
|
||||
n.us = wf.Userstamps()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *report) Encode(ctx context.Context, doc *Document, state *envoy.ResourceState) (err error) {
|
||||
if n.res.ID <= 0 {
|
||||
n.res.ID = nextID()
|
||||
}
|
||||
|
||||
n.ts, err = resource.MakeTimestampsCUDA(&n.res.CreatedAt, n.res.UpdatedAt, n.res.DeletedAt, nil).
|
||||
Model(n.encoderConfig.TimeLayout, n.encoderConfig.Timezone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.us, err = resolveUserstamps(state.ParentResources, n.us)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// @todo skip eval?
|
||||
|
||||
doc.addReport(n)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (wf *report) MarshalYAML() (interface{}, error) {
|
||||
var err error
|
||||
|
||||
nn, err := makeMap(
|
||||
"handle", wf.res.Handle,
|
||||
"meta", wf.res.Meta,
|
||||
|
||||
"sources", wf.sources,
|
||||
"projections", wf.projections,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nn, err = encodeTimestamps(nn, wf.ts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nn, err = encodeUserstamps(nn, wf.us)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (t *reportSource) MarshalYAML() (interface{}, error) {
|
||||
var err error
|
||||
|
||||
nn, err := makeMap(
|
||||
"meta", t.res.Meta,
|
||||
"step", t.res.Step,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (t *reportProjection) MarshalYAML() (interface{}, error) {
|
||||
var err error
|
||||
|
||||
nn, err := makeMap(
|
||||
"title", t.res.Title,
|
||||
"description", t.res.Description,
|
||||
"key", t.res.Key,
|
||||
"kind", t.res.Kind,
|
||||
"options", t.res.Options,
|
||||
"elements", t.res.Elements,
|
||||
"sources", t.res.Sources,
|
||||
"xywh", t.res.XYWH,
|
||||
"layout", t.res.Layout,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
"github.com/cortezaproject/corteza-server/pkg/y7s"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func (wset *reportSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return y7s.Each(n, func(k, v *yaml.Node) (err error) {
|
||||
var (
|
||||
wrap = &report{}
|
||||
)
|
||||
|
||||
if v == nil {
|
||||
return y7s.NodeErr(n, "malformed report definition")
|
||||
}
|
||||
|
||||
if err = v.Decode(&wrap); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = decodeRef(k, "report handle", &wrap.res.Handle); err != nil {
|
||||
return y7s.NodeErr(n, "Report reference must be a valid handle")
|
||||
}
|
||||
|
||||
if wrap.res.Meta == nil {
|
||||
wrap.res.Meta = &types.ReportMeta{}
|
||||
}
|
||||
if wrap.res.Meta.Name == "" {
|
||||
// if name is not set, use handle
|
||||
wrap.res.Meta.Name = wrap.res.Handle
|
||||
}
|
||||
|
||||
*wset = append(*wset, wrap)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap *report) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
if wrap.res == nil {
|
||||
wrap.rbac = make(rbacRuleSet, 0, 10)
|
||||
wrap.res = &types.Report{}
|
||||
}
|
||||
|
||||
if wrap.rbac, err = decodeRbac(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.locale, err = decodeLocale(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.envoyConfig, err = decodeEnvoyConfig(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.ts, err = decodeTimestamps(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.us, err = decodeUserstamps(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch strings.ToLower(k.Value) {
|
||||
case "handle":
|
||||
return y7s.DecodeScalar(v, "report handle", &wrap.res.Handle)
|
||||
|
||||
case "meta":
|
||||
return v.Decode(&wrap.res.Meta)
|
||||
|
||||
case "sources":
|
||||
wrap.sources = make(reportSourceSet, 0, 10)
|
||||
|
||||
err = v.Decode(&wrap.sources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "projections":
|
||||
wrap.projections = make(reportProjectionSet, 0, 10)
|
||||
|
||||
err = v.Decode(&wrap.projections)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap *reportSource) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
if wrap.res == nil {
|
||||
wrap.res = &types.ReportDataSource{}
|
||||
}
|
||||
|
||||
if wrap.envoyConfig, err = decodeEnvoyConfig(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch k.Value {
|
||||
case "meta":
|
||||
return v.Decode(&wrap.res.Meta)
|
||||
case "step":
|
||||
return v.Decode(&wrap.res.Step)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap *reportProjection) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
if wrap.res == nil {
|
||||
wrap.res = &types.ReportProjection{}
|
||||
}
|
||||
|
||||
if wrap.envoyConfig, err = decodeEnvoyConfig(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wrap.locale, err = decodeLocale(n); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch strings.ToLower(k.Value) {
|
||||
case "title":
|
||||
return y7s.DecodeScalar(v, "title", &wrap.res.Title)
|
||||
case "description":
|
||||
return y7s.DecodeScalar(v, "description", &wrap.res.Description)
|
||||
case "key":
|
||||
return y7s.DecodeScalar(v, "key", &wrap.res.Key)
|
||||
case "kind":
|
||||
return y7s.DecodeScalar(v, "kind", &wrap.res.Kind)
|
||||
case "options":
|
||||
return v.Decode(&wrap.res.Options)
|
||||
case "elements":
|
||||
return v.Decode(&wrap.res.Elements)
|
||||
case "sources":
|
||||
return v.Decode(&wrap.res.Sources)
|
||||
case "xywh":
|
||||
return v.Decode(&wrap.res.XYWH)
|
||||
case "layout":
|
||||
return y7s.DecodeScalar(v, "layout", &wrap.res.Layout)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wset reportSet) MarshalEnvoy() ([]resource.Interface, error) {
|
||||
nn := make([]resource.Interface, 0, len(wset)*2)
|
||||
|
||||
for _, res := range wset {
|
||||
if tmp, err := res.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (wrap report) MarshalEnvoy() ([]resource.Interface, error) {
|
||||
rs := resource.NewReport(wrap.res)
|
||||
rs.SetTimestamps(wrap.ts)
|
||||
rs.SetUserstamps(wrap.us)
|
||||
rs.SetConfig(wrap.envoyConfig)
|
||||
|
||||
// default report translations
|
||||
// includes translations for nested resources also
|
||||
var defaultReportTranslations []resource.Interface
|
||||
dft, err := rs.EncodeTranslations()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, d := range dft {
|
||||
d.MarkDefault()
|
||||
defaultReportTranslations = append(defaultReportTranslations, d)
|
||||
}
|
||||
|
||||
for _, s := range wrap.sources {
|
||||
rs.AddReportSource(s.res)
|
||||
}
|
||||
|
||||
for _, p := range wrap.projections {
|
||||
rs.AddReportProjection(p.res)
|
||||
}
|
||||
|
||||
return envoy.CollectNodes(
|
||||
rs,
|
||||
defaultReportTranslations,
|
||||
wrap.rbac.bindResource(rs),
|
||||
)
|
||||
}
|
||||
@@ -46,7 +46,6 @@ func resourceTranslationFromResource(r *resource.ResourceTranslation, cfg *Encod
|
||||
if len(r.Res) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &resourceTranslation{
|
||||
locales: r.Res,
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
@@ -192,6 +193,17 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
|
||||
// return fmt.Sprintf(automationTypes.WorkflowResourceTranslationTpl(), automationTypes.WorkflowResourceTranslationType, p0ID), nil
|
||||
|
||||
case systemTypes.ReportResourceType:
|
||||
if res.RefRes != nil {
|
||||
p0 := resource.FindReport(state.ParentResources, res.RefRes.Identifiers)
|
||||
if p0 == nil {
|
||||
return "", resource.ReportErrUnresolved(res.RefRes.Identifiers)
|
||||
}
|
||||
p0ID = p0.Handle
|
||||
}
|
||||
|
||||
return fmt.Sprintf(systemTypes.ReportResourceTranslationTpl(), systemTypes.ReportResourceTranslationType, p0ID), nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported resource type '%s' for locale resource YAML encode", r.refLocaleRes.ResourceType)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package minions
|
||||
|
||||
import "reflect"
|
||||
|
||||
// IsNil checks if the given interface is truly nil
|
||||
//
|
||||
// Due to how interfaces are handled under-the-hood, a simple i == nil may
|
||||
// not always be ok.
|
||||
func IsNil(i interface{}) bool {
|
||||
if i == nil {
|
||||
return true
|
||||
}
|
||||
switch reflect.TypeOf(i).Kind() {
|
||||
case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice:
|
||||
return reflect.ValueOf(i).IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -106,6 +106,9 @@ func truncateStore(ctx context.Context, s store.Storer, t *testing.T) {
|
||||
s.TruncateUsers(ctx),
|
||||
s.TruncateTemplates(ctx),
|
||||
s.TruncateApplications(ctx),
|
||||
s.TruncateApigwRoutes(ctx),
|
||||
s.TruncateApigwFilters(ctx),
|
||||
s.TruncateReports(ctx),
|
||||
s.TruncateSettings(ctx),
|
||||
s.TruncateRbacRules(ctx),
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/yaml"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStoreYaml_APIGateway(t *testing.T) {
|
||||
type (
|
||||
tc struct {
|
||||
name string
|
||||
// Before the data gets processed
|
||||
pre func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter)
|
||||
// After the data gets processed
|
||||
postStoreDecode func(req *require.Assertions, err error)
|
||||
postYamlEncode func(req *require.Assertions, err error)
|
||||
postStoreEncode func(req *require.Assertions, err error)
|
||||
// Data assertions
|
||||
check func(ctx context.Context, s store.Storer, req *require.Assertions)
|
||||
}
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
s := initServices(ctx, t)
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.ServiceUser())
|
||||
|
||||
ni := uint64(10)
|
||||
su.NextID = func() uint64 {
|
||||
ni++
|
||||
return ni
|
||||
}
|
||||
|
||||
cases := []*tc{
|
||||
{
|
||||
name: "base",
|
||||
pre: func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter) {
|
||||
gwr := sTestAPIGatewayRoute(ctx, t, s, "test")
|
||||
_ = sTestAPIGatewayFilter(ctx, t, s, gwr.ID, "test")
|
||||
|
||||
df := su.NewDecodeFilter().
|
||||
APIGWRoutes(&types.ApigwRouteFilter{Enabled: true})
|
||||
|
||||
return nil, df
|
||||
},
|
||||
check: func(ctx context.Context, s store.Storer, req *require.Assertions) {
|
||||
rr, _, err := store.SearchApigwRoutes(ctx, s, types.ApigwRouteFilter{})
|
||||
req.NoError(err)
|
||||
req.Len(rr, 1)
|
||||
|
||||
r := rr[0]
|
||||
req.Equal("/testing/test", r.Endpoint)
|
||||
req.Equal("POST", r.Method)
|
||||
req.True(r.Enabled)
|
||||
req.True(r.Meta.Debug)
|
||||
req.True(r.Meta.Async)
|
||||
|
||||
ff, _, err := store.SearchApigwFilters(ctx, s, types.ApigwFilterFilter{RouteID: rr[0].ID})
|
||||
req.NoError(err)
|
||||
req.Len(ff, 1)
|
||||
|
||||
f := ff[0]
|
||||
req.Equal(r.ID, f.Route)
|
||||
req.Equal("test_ref", f.Ref)
|
||||
req.Equal("test_kind", f.Kind)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
truncateStore(ctx, s, t)
|
||||
err, df := c.pre(ctx, s)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
// Decode from store
|
||||
sd := su.Decoder()
|
||||
nn, err := sd.Decode(ctx, s, df)
|
||||
if c.postStoreDecode != nil {
|
||||
c.postStoreDecode(req, err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// Encode into YAML
|
||||
ye := yaml.NewYamlEncoder(&yaml.EncoderConfig{})
|
||||
bld := envoy.NewBuilder(ye)
|
||||
g, err := bld.Build(ctx, nn...)
|
||||
req.NoError(err)
|
||||
err = envoy.Encode(ctx, g, ye)
|
||||
ss := ye.Stream()
|
||||
if c.postYamlEncode != nil {
|
||||
c.postYamlEncode(req, err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// Cleanup the store
|
||||
truncateStore(ctx, s, t)
|
||||
|
||||
// Encode back into store
|
||||
se := su.NewStoreEncoder(s, &su.EncoderConfig{})
|
||||
yd := yaml.Decoder()
|
||||
nn = make([]resource.Interface, 0, len(nn))
|
||||
for _, s := range ss {
|
||||
mm, err := yd.Decode(ctx, s.Source, nil)
|
||||
req.NoError(err)
|
||||
nn = append(nn, mm...)
|
||||
}
|
||||
bld = envoy.NewBuilder(se)
|
||||
g, err = bld.Build(ctx, nn...)
|
||||
req.NoError(err)
|
||||
|
||||
err = envoy.Encode(ctx, g, se)
|
||||
if c.postStoreEncode != nil {
|
||||
c.postStoreEncode(req, err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
c.check(ctx, s, req)
|
||||
|
||||
// Cleanup the store
|
||||
truncateStore(ctx, s, t)
|
||||
})
|
||||
ni = 0
|
||||
truncateStore(ctx, s, t)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/yaml"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -41,6 +42,75 @@ func TestStoreYaml_moduleFieldRefs(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []*tc{
|
||||
{
|
||||
name: "user field; role filter",
|
||||
pre: func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter) {
|
||||
ns := sTestComposeNamespace(ctx, t, s, "base")
|
||||
rl := sTestRole(ctx, t, s, "base")
|
||||
|
||||
modID := su.NextID()
|
||||
mod := &types.Module{
|
||||
ID: modID,
|
||||
Name: "usr_rl",
|
||||
Handle: "usr_rl",
|
||||
NamespaceID: ns.ID,
|
||||
Fields: types.ModuleFieldSet{
|
||||
&types.ModuleField{
|
||||
ID: su.NextID(),
|
||||
ModuleID: modID,
|
||||
Kind: "User",
|
||||
Place: 0,
|
||||
Name: "usr_rl",
|
||||
Options: types.ModuleFieldOptions{
|
||||
"roles": []string{strconv.FormatUint(rl.ID, 10)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := store.CreateComposeModule(ctx, s, mod)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = store.CreateComposeModuleField(ctx, s, mod.Fields...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
df := su.NewDecodeFilter().
|
||||
ComposeNamespace(&types.NamespaceFilter{
|
||||
Slug: "base_namespace",
|
||||
}).
|
||||
Roles(&systemTypes.RoleFilter{}).
|
||||
ComposeModule(&types.ModuleFilter{
|
||||
NamespaceID: ns.ID,
|
||||
})
|
||||
return nil, df
|
||||
},
|
||||
check: func(ctx context.Context, s store.Storer, req *require.Assertions) {
|
||||
n, err := store.LookupComposeNamespaceBySlug(ctx, s, "base_namespace")
|
||||
req.NoError(err)
|
||||
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, n.ID, "usr_rl")
|
||||
req.NoError(err)
|
||||
role, err := store.LookupRoleByHandle(ctx, s, "base_role")
|
||||
req.NoError(err)
|
||||
req.NotNil(role)
|
||||
|
||||
mff, _, err := store.SearchComposeModuleFields(ctx, s, types.ModuleFieldFilter{
|
||||
ModuleID: []uint64{mod.ID},
|
||||
})
|
||||
req.NoError(err)
|
||||
|
||||
// Check module relations for Options.module variant
|
||||
f := mff.FindByName("usr_rl")
|
||||
req.NotNil(f)
|
||||
|
||||
rr := f.Options["roles"].([]interface{})
|
||||
req.Len(rr, 1)
|
||||
req.Equal(strconv.FormatUint(role.ID, 10), rr[0])
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "external module ref",
|
||||
pre: func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter) {
|
||||
|
||||
@@ -112,6 +112,72 @@ func TestStoreYaml_pageRefs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "pageblock comment",
|
||||
pre: func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter) {
|
||||
ns := sTestComposeNamespace(ctx, t, s, "base")
|
||||
mod := sTestComposeModule(ctx, t, s, ns.ID, "base")
|
||||
|
||||
pg := &types.Page{
|
||||
ID: su.NextID(),
|
||||
NamespaceID: ns.ID,
|
||||
Handle: "page",
|
||||
Title: "page",
|
||||
Blocks: types.PageBlocks{
|
||||
{
|
||||
Title: "comment_1",
|
||||
Kind: "Comment",
|
||||
Options: map[string]interface{}{
|
||||
"module": strconv.FormatUint(mod.ID, 10),
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "comment_2",
|
||||
Kind: "Comment",
|
||||
Options: map[string]interface{}{
|
||||
"moduleID": strconv.FormatUint(mod.ID, 10),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := store.CreateComposePage(ctx, s, pg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
df := su.NewDecodeFilter().
|
||||
ComposeNamespace(&types.NamespaceFilter{
|
||||
Slug: "base_namespace",
|
||||
}).
|
||||
ComposeModule(&types.ModuleFilter{
|
||||
NamespaceID: ns.ID,
|
||||
}).
|
||||
ComposePage(&types.PageFilter{
|
||||
NamespaceID: ns.ID,
|
||||
})
|
||||
return nil, df
|
||||
},
|
||||
check: func(ctx context.Context, s store.Storer, req *require.Assertions) {
|
||||
n, err := store.LookupComposeNamespaceBySlug(ctx, s, "base_namespace")
|
||||
req.NoError(err)
|
||||
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, n.ID, "base_module")
|
||||
req.NoError(err)
|
||||
|
||||
pg, err := store.LookupComposePageByNamespaceIDHandle(ctx, s, n.ID, "page")
|
||||
req.NoError(err)
|
||||
req.Len(pg.Blocks, 2)
|
||||
|
||||
// provided as module
|
||||
b := pg.Blocks[0]
|
||||
req.Equal(strconv.FormatUint(mod.ID, 10), b.Options["moduleID"])
|
||||
|
||||
// provided as moduleID
|
||||
b = pg.Blocks[1]
|
||||
req.Equal(strconv.FormatUint(mod.ID, 10), b.Options["moduleID"])
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "pageblock; automation",
|
||||
pre: func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter) {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
|
||||
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/yaml"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStoreYaml_reports(t *testing.T) {
|
||||
type (
|
||||
tc struct {
|
||||
name string
|
||||
// Before the data gets processed
|
||||
pre func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter)
|
||||
// After the data gets processed
|
||||
postStoreDecode func(req *require.Assertions, err error)
|
||||
postYamlEncode func(req *require.Assertions, err error)
|
||||
postStoreEncode func(req *require.Assertions, err error)
|
||||
// Data assertions
|
||||
check func(ctx context.Context, s store.Storer, req *require.Assertions)
|
||||
}
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
s := initServices(ctx, t)
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.ServiceUser())
|
||||
|
||||
ni := uint64(10)
|
||||
su.NextID = func() uint64 {
|
||||
ni++
|
||||
return ni
|
||||
}
|
||||
|
||||
cases := []*tc{
|
||||
{
|
||||
name: "base",
|
||||
pre: func(ctx context.Context, s store.Storer) (error, *su.DecodeFilter) {
|
||||
sTestReport(ctx, t, s, "test")
|
||||
|
||||
df := su.NewDecodeFilter().
|
||||
Reports(&types.ReportFilter{})
|
||||
|
||||
return nil, df
|
||||
},
|
||||
check: func(ctx context.Context, s store.Storer, req *require.Assertions) {
|
||||
rr, _, err := store.SearchReports(ctx, s, types.ReportFilter{})
|
||||
req.NoError(err)
|
||||
req.Len(rr, 1)
|
||||
|
||||
r := rr[0]
|
||||
req.Equal("test_report", r.Handle)
|
||||
req.Equal("test report", r.Meta.Name)
|
||||
req.Equal("testing", r.Meta.Description)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
truncateStore(ctx, s, t)
|
||||
err, df := c.pre(ctx, s)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
// Decode from store
|
||||
sd := su.Decoder()
|
||||
nn, err := sd.Decode(ctx, s, df)
|
||||
if c.postStoreDecode != nil {
|
||||
c.postStoreDecode(req, err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// Encode into YAML
|
||||
ye := yaml.NewYamlEncoder(&yaml.EncoderConfig{})
|
||||
bld := envoy.NewBuilder(ye)
|
||||
g, err := bld.Build(ctx, nn...)
|
||||
req.NoError(err)
|
||||
err = envoy.Encode(ctx, g, ye)
|
||||
ss := ye.Stream()
|
||||
if c.postYamlEncode != nil {
|
||||
c.postYamlEncode(req, err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// Cleanup the store
|
||||
truncateStore(ctx, s, t)
|
||||
|
||||
// Encode back into store
|
||||
se := su.NewStoreEncoder(s, &su.EncoderConfig{})
|
||||
yd := yaml.Decoder()
|
||||
nn = make([]resource.Interface, 0, len(nn))
|
||||
for _, s := range ss {
|
||||
mm, err := yd.Decode(ctx, s.Source, nil)
|
||||
req.NoError(err)
|
||||
nn = append(nn, mm...)
|
||||
}
|
||||
bld = envoy.NewBuilder(se)
|
||||
g, err = bld.Build(ctx, nn...)
|
||||
req.NoError(err)
|
||||
|
||||
err = envoy.Encode(ctx, g, se)
|
||||
if c.postStoreEncode != nil {
|
||||
c.postStoreEncode(req, err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
c.check(ctx, s, req)
|
||||
|
||||
// Cleanup the store
|
||||
truncateStore(ctx, s, t)
|
||||
})
|
||||
ni = 0
|
||||
truncateStore(ctx, s, t)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza-server/pkg/report"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
@@ -161,3 +162,88 @@ func sTestRbac(ctx context.Context, t *testing.T, s store.Storer, roleID uint64)
|
||||
|
||||
return rr
|
||||
}
|
||||
|
||||
func sTestReport(ctx context.Context, t *testing.T, s store.Storer, pfx string) *types.Report {
|
||||
r := &types.Report{
|
||||
ID: su.NextID(),
|
||||
Handle: pfx + "_report",
|
||||
Meta: &types.ReportMeta{Name: pfx + " report", Description: "testing"},
|
||||
|
||||
Sources: types.ReportDataSourceSet{{
|
||||
Meta: map[string]interface{}{"key1": "value1"},
|
||||
Step: &report.StepDefinition{
|
||||
Kind: "Load",
|
||||
Load: &report.LoadStepDefinition{
|
||||
Name: "Test",
|
||||
Source: "test",
|
||||
Definition: map[string]interface{}{
|
||||
"k1": "v1",
|
||||
},
|
||||
Columns: report.FrameColumnSet{{Name: "col1", Label: "col1 label"}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
Projections: types.ReportProjectionSet{{
|
||||
Title: "title",
|
||||
Description: "description",
|
||||
Key: "key",
|
||||
Kind: "kind",
|
||||
Options: map[string]interface{}{
|
||||
"k1": "v1",
|
||||
},
|
||||
Elements: []interface{}{
|
||||
map[string]interface{}{"k1": "v1"},
|
||||
},
|
||||
XYWH: [4]int{1, 2, 3, 4},
|
||||
Layout: "layout",
|
||||
}},
|
||||
}
|
||||
|
||||
err := store.CreateReport(ctx, s, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func sTestAPIGatewayRoute(ctx context.Context, t *testing.T, s store.Storer, r string) *types.ApigwRoute {
|
||||
gwr := &types.ApigwRoute{
|
||||
ID: su.NextID(),
|
||||
Endpoint: "/testing/" + r,
|
||||
Method: "POST",
|
||||
Enabled: true,
|
||||
Group: 0,
|
||||
Meta: types.ApigwRouteMeta{
|
||||
Debug: true,
|
||||
Async: true,
|
||||
},
|
||||
}
|
||||
|
||||
err := store.CreateApigwRoute(ctx, s, gwr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return gwr
|
||||
}
|
||||
|
||||
func sTestAPIGatewayFilter(ctx context.Context, t *testing.T, s store.Storer, routeID uint64, pfx string) *types.ApigwFilter {
|
||||
gwf := &types.ApigwFilter{
|
||||
ID: su.NextID(),
|
||||
Route: routeID,
|
||||
Weight: 0,
|
||||
Ref: pfx + "_ref",
|
||||
Kind: pfx + "_kind",
|
||||
Params: map[string]interface{}{
|
||||
"param1": "value1",
|
||||
},
|
||||
}
|
||||
|
||||
err := store.CreateApigwFilter(ctx, s, gwf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return gwf
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user