3
0

Improve envoy system values

Handle timestamps & user stamps.
This commit is contained in:
Tomaž Jerman
2020-12-02 15:07:13 +01:00
parent 6b928d3a18
commit a2110b26db
29 changed files with 886 additions and 108 deletions

View File

@@ -5,12 +5,30 @@ type (
rt string
ii Identifiers
rr RefSet
ts *Timestamps
us *Userstamps
urefs RefSet
}
EnvoyConfig struct {
// SkipIf determines when the encoding should be skipped for this resource
SkipIf string
}
Timestamps struct {
CreatedAt string
UpdatedAt string
DeletedAt string
ArchivedAt string
SuspendedAt string
}
Userstamps struct {
CreatedBy string
UpdatedBy string
DeletedBy string
OwnedBy string
}
)
// State management methods
@@ -48,6 +66,40 @@ func (t *base) SetResourceType(rt string) {
t.rt = rt
}
func (t *base) SetTimestamps(ts *Timestamps) {
t.ts = ts
}
func (t *base) Timestamps() *Timestamps {
return t.ts
}
func (t *base) SetUserstamps(us *Userstamps) {
t.us = us
if us != nil {
uu := []string{us.CreatedBy, us.UpdatedBy, us.DeletedBy, us.OwnedBy}
t.SetUserRefs(uu)
}
}
func (t *base) Userstamps() *Userstamps {
return t.us
}
func (t *base) SetUserRefs(uu []string) {
if t.urefs == nil {
t.urefs = make(RefSet, 0, 4)
}
for _, u := range uu {
if u != "" {
t.urefs = append(t.urefs, t.AddRef(USER_RESOURCE_TYPE, u))
}
}
}
func (t *base) UserRefs() RefSet {
return t.urefs
}
// Resource interface methods
func (t *base) Identifiers() Identifiers {

View File

@@ -1,8 +1,6 @@
package resource
import (
"strings"
"github.com/cortezaproject/corteza-server/compose/types"
)
@@ -17,10 +15,12 @@ type (
DeletedBy string
}
ComposeRecordRaw struct {
ID string
Values map[string]string
SysValues *rawSysValues
RefUsers map[string]string
ID string
Values map[string]string
Ts *Timestamps
Us *Userstamps
RefUsers map[string]string
Config *EnvoyConfig
}
@@ -58,42 +58,3 @@ func NewComposeRecordSet(w crsWalker, nsRef, modRef string) *ComposeRecord {
return r
}
// ApplyValues takes in a raw map of things and creates a proper structure for it
func (cr *ComposeRecordRaw) ApplyValues(vv map[string]string) {
if cr.RefUsers == nil {
cr.RefUsers = make(map[string]string)
}
if cr.SysValues == nil {
cr.SysValues = &rawSysValues{}
}
if cr.Values == nil {
cr.Values = make(map[string]string)
}
for k, v := range vv {
switch strings.ToLower(k) {
case "ownedby":
cr.SysValues.OwnedBy = v
cr.RefUsers[v] = ""
case "createdat":
cr.SysValues.CreatedAt = v
case "createdby":
cr.SysValues.CreatedBy = v
cr.RefUsers[v] = ""
case "updatedat":
cr.SysValues.UpdatedAt = v
case "updatedby":
cr.SysValues.UpdatedBy = v
cr.RefUsers[v] = ""
case "deletedat":
cr.SysValues.DeletedAt = v
case "deletedby":
cr.SysValues.DeletedBy = v
cr.RefUsers[v] = ""
default:
cr.Values[k] = v
}
}
}

View File

@@ -47,9 +47,9 @@ func (crt *composeRecordShaper) toResource(def *ComposeRecordTemplate, dt *Resou
// Get the bits in order
rRaw := &ComposeRecordRaw{
ID: crt.getKey(mr, def.Key),
ID: crt.getKey(mr, def.Key),
Values: crt.mapValues(mr, def.FieldMap),
}
rRaw.ApplyValues(crt.mapValues(mr, def.FieldMap))
// Process it
err = f(rRaw)

View File

@@ -65,6 +65,23 @@ func (n *applicationState) Encode(ctx context.Context, s store.Storer, state *en
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
}
// Create fresh application
if !exists {
return store.CreateApplication(ctx, s, res)

View File

@@ -87,6 +87,23 @@ func (n *composeChartState) Encode(ctx context.Context, s store.Storer, state *e
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
}
// Namespace
res.NamespaceID = n.relNS.ID
if res.NamespaceID <= 0 {

View File

@@ -115,6 +115,23 @@ func (n *composeModuleState) Encode(ctx context.Context, s store.Storer, state *
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
}
// Namespace
res.NamespaceID = n.relNS.ID
if res.NamespaceID <= 0 {

View File

@@ -63,6 +63,23 @@ func (n *composeNamespaceState) Encode(ctx context.Context, s store.Storer, stat
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
}
// Create a fresh namespace
if !exists {
return store.CreateComposeNamespace(ctx, s, res)

View File

@@ -134,6 +134,23 @@ func (n *composePageState) Encode(ctx context.Context, s store.Storer, state *en
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
}
// Namespace
res.NamespaceID = n.relNS.ID
if res.NamespaceID <= 0 {

View File

@@ -21,6 +21,7 @@ type (
relNS *types.Namespace
relMod *types.Module
relUsr map[string]uint64
// Little helper flag for conditional encoding
missing bool
@@ -81,6 +82,12 @@ func (n *composeRecordState) Prepare(ctx context.Context, s store.Storer, state
return composeModuleErrUnresolved(n.res.ModRef.Identifiers)
}
// Sys users
n.relUsr = make(map[string]uint64)
if err = resolveUserRefs(ctx, s, state.ParentResources, n.res.UserRefs(), n.relUsr); err != nil {
return err
}
// Add missing refs
for _, f := range n.relMod.Fields {
switch f.Kind {
@@ -140,6 +147,15 @@ func (n *composeRecordState) Encode(ctx context.Context, s store.Storer, state *
mod.ID = m.ID
}
// Sys users
for idf, ID := range n.relUsr {
if ID != 0 {
continue
}
u := findUserR(ctx, state.ParentResources, resource.MakeIdentifiers(idf))
n.relUsr[idf] = u.ID
}
// Some pointing
rm := n.res.RecMap
im := n.res.IDMap
@@ -211,11 +227,36 @@ func (n *composeRecordState) Encode(ctx context.Context, s store.Storer, state *
return nil
}
// Sys values
// @todo
//
// for k, v := range r.SysValues {
// }
// Timestamps
if r.Ts != nil {
if r.Ts.CreatedAt != "" {
t := toTime(r.Ts.CreatedAt)
if t != nil {
rec.CreatedAt = *t
}
}
if r.Ts.UpdatedAt != "" {
rec.UpdatedAt = toTime(r.Ts.UpdatedAt)
}
if r.Ts.DeletedAt != "" {
rec.DeletedAt = toTime(r.Ts.DeletedAt)
}
}
// Userstamps
if r.Us != nil {
if r.Us.CreatedBy != "" {
rec.CreatedBy = n.relUsr[r.Us.CreatedBy]
}
if r.Us.UpdatedBy != "" {
rec.UpdatedBy = n.relUsr[r.Us.UpdatedBy]
}
if r.Us.DeletedBy != "" {
rec.DeletedBy = n.relUsr[r.Us.DeletedBy]
}
if r.Us.OwnedBy != "" {
rec.OwnedBy = n.relUsr[r.Us.OwnedBy]
}
}
rvs := make(types.RecordValueSet, 0, len(r.Values))
for k, v := range r.Values {

View File

@@ -2,9 +2,10 @@ package store
import (
"context"
"github.com/cortezaproject/corteza-server/messaging/types"
"time"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/pkg/envoy"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
"github.com/cortezaproject/corteza-server/store"
@@ -16,6 +17,8 @@ type (
res *resource.MessagingChannel
ch *types.Channel
relUsr map[string]uint64
}
)
@@ -32,6 +35,12 @@ func (n *messagingChannelState) Prepare(ctx context.Context, s store.Storer, rs
n.res.Res.CreatedAt = time.Now()
}
// Sys users
n.relUsr = make(map[string]uint64)
if err = resolveUserRefs(ctx, s, rs.ParentResources, n.res.UserRefs(), n.relUsr); err != nil {
return err
}
// Get the existing channel
n.ch, err = findMessagingChannelS(ctx, s, makeGenericFilter(n.res.Identifiers()))
if err != nil {
@@ -57,11 +66,48 @@ func (n *messagingChannelState) Encode(ctx context.Context, s store.Storer, stat
res.ID = NextID()
}
// Sys users
for idf, ID := range n.relUsr {
if ID != 0 {
continue
}
u := findUserR(ctx, state.ParentResources, resource.MakeIdentifiers(idf))
n.relUsr[idf] = u.ID
}
// This is not possible, but let's do it anyway
if state.Conflicting {
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
if ts.ArchivedAt != "" {
res.ArchivedAt = toTime(ts.ArchivedAt)
}
}
// Userstamps
us := n.res.Userstamps()
if us != nil {
if us.CreatedBy != "" {
res.CreatorID = n.relUsr[us.CreatedBy]
}
}
// Create fresh messagingChannel
if !exists {
return store.CreateMessagingChannel(ctx, s, res)

View File

@@ -62,6 +62,26 @@ func (n *roleState) Encode(ctx context.Context, s store.Storer, state *envoy.Res
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
rl.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
rl.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
rl.DeletedAt = toTime(ts.DeletedAt)
}
if ts.ArchivedAt != "" {
rl.ArchivedAt = toTime(ts.ArchivedAt)
}
}
// Create a fresh role
if !exists {
return store.CreateRole(ctx, s, rl)

View File

@@ -2,6 +2,7 @@ package store
import (
"context"
"fmt"
"time"
"github.com/cortezaproject/corteza-server/pkg/envoy"
@@ -63,6 +64,26 @@ func (n *userState) Encode(ctx context.Context, s store.Storer, state *envoy.Res
return nil
}
// Timestamps
ts := n.res.Timestamps()
if ts != nil {
if ts.CreatedAt != "" {
t := toTime(ts.CreatedAt)
if t != nil {
res.CreatedAt = *t
}
}
if ts.UpdatedAt != "" {
res.UpdatedAt = toTime(ts.UpdatedAt)
}
if ts.DeletedAt != "" {
res.DeletedAt = toTime(ts.DeletedAt)
}
if ts.SuspendedAt != "" {
res.SuspendedAt = toTime(ts.SuspendedAt)
}
}
// Create a fresh user
if !exists {
return store.CreateUser(ctx, s, res)
@@ -164,7 +185,7 @@ func findUserR(ctx context.Context, rr resource.InterfaceSet, ii resource.Identi
return nil
}
if uRes.Identifiers().HasAny(ii) {
if ur.Identifiers().HasAny(ii) {
uRes = ur
}
return nil
@@ -177,3 +198,7 @@ func findUserR(ctx context.Context, rr resource.InterfaceSet, ii resource.Identi
return nil
}
func userErrUnresolved(ii resource.Identifiers) error {
return fmt.Errorf("user unresolved %v", ii.StringSlice())
}

View File

@@ -1,13 +1,16 @@
package store
import (
"context"
"regexp"
"strconv"
"time"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/cortezaproject/corteza-server/pkg/handle"
"github.com/cortezaproject/corteza-server/pkg/id"
"github.com/cortezaproject/corteza-server/store"
)
type (
@@ -55,3 +58,50 @@ func makeGenericFilter(ii resource.Identifiers) (f genericFilter) {
return f
}
// Taken from compose/service/values/sanitizer.go
func toTime(v string) *time.Time {
ff := []string{
time.RFC3339,
time.RFC1123Z,
time.RFC1123,
time.RFC850,
time.RFC822Z,
time.RFC822,
time.RubyDate,
time.UnixDate,
time.ANSIC,
"2006/_1/_2 15:04:05",
"2006/_1/_2 15:04",
}
for _, f := range ff {
parsed, err := time.Parse(f, v)
if err == nil {
return &parsed
}
}
return nil
}
func resolveUserRefs(ctx context.Context, s store.Storer, pr []resource.Interface, refs resource.RefSet, dst map[string]uint64) (err error) {
for _, uRef := range refs {
u := findUserR(ctx, pr, uRef.Identifiers)
if u == nil {
u, err = findUserS(ctx, s, makeGenericFilter(uRef.Identifiers))
if err != nil {
return err
}
}
if u == nil {
return userErrUnresolved(uRef.Identifiers)
}
// Unexisting users will have ID 0, but that's ok, as long as they exist
for i := range uRef.Identifiers {
dst[i] = u.ID
}
}
return nil
}

View File

@@ -11,6 +11,7 @@ type (
application struct {
// when application is at least partially defined
res *types.Application `yaml:",inline"`
ts *resource.Timestamps
// module's RBAC rules
rbac rbacRuleSet
@@ -43,7 +44,7 @@ func (wset *applicationSet) UnmarshalYAML(n *yaml.Node) error {
}
case yaml.MappingNode:
if err = v.Decode(&wrap.res); err != nil {
if err = v.Decode(&wrap); err != nil {
return
}
@@ -77,6 +78,9 @@ func (wrap *application) UnmarshalYAML(n *yaml.Node) (err error) {
if wrap.res == nil {
wrap.res = &types.Application{}
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
if err = n.Decode(&wrap.res); err != nil {
return
@@ -91,6 +95,8 @@ func (wrap *application) UnmarshalYAML(n *yaml.Node) (err error) {
func (wrap application) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewApplication(wrap.res)
rs.SetTimestamps(wrap.ts)
return envoy.CollectNodes(
rs,
wrap.rbac.bindResource(rs),

View File

@@ -12,6 +12,7 @@ import (
type (
composeChart struct {
res *types.Chart
ts *resource.Timestamps
refNamespace string
// pointer to report and module reference
@@ -97,6 +98,10 @@ func (wrap *composeChart) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
return eachMap(n, func(k, v *yaml.Node) (err error) {
switch k.Value {
case "handle":
@@ -129,6 +134,7 @@ func (wrap composeChart) MarshalEnvoy() ([]resource.Interface, error) {
vv = append(vv, v)
}
rs := resource.NewComposeChart(wrap.res, wrap.refNamespace, vv)
rs.SetTimestamps(wrap.ts)
return envoy.CollectNodes(
rs,
wrap.rbac.bindResource(rs),

View File

@@ -25,6 +25,7 @@ type (
composeModule struct {
res *types.Module
ts *resource.Timestamps
refNamespace string
rbac rbacRuleSet
@@ -107,6 +108,10 @@ func (wrap *composeModule) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
return eachMap(n, func(k, v *yaml.Node) (err error) {
switch k.Value {
case "name":
@@ -146,6 +151,7 @@ func (wrap *composeModule) UnmarshalYAML(n *yaml.Node) (err error) {
func (wrap composeModule) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewComposeModule(wrap.res, wrap.refNamespace)
rs.SetTimestamps(wrap.ts)
var crs *resource.ComposeRecordTemplate
if wrap.recTpl != nil {

View File

@@ -12,6 +12,7 @@ type (
composeNamespace struct {
// when namespace is at least partially defined
res *types.Namespace `yaml:",inline"`
ts *resource.Timestamps
// all known modules on a namespace
modules composeModuleSet
@@ -103,6 +104,10 @@ func (wrap *composeNamespace) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
return each(n, func(k, v *yaml.Node) (err error) {
switch k.Value {
case "modules":
@@ -125,6 +130,8 @@ func (wrap *composeNamespace) UnmarshalYAML(n *yaml.Node) (err error) {
func (wrap composeNamespace) MarshalEnvoy() ([]resource.Interface, error) {
nsr := resource.NewComposeNamespace(wrap.res)
nsr.SetTimestamps(wrap.ts)
return envoy.CollectNodes(
nsr,
wrap.modules,

View File

@@ -12,6 +12,7 @@ import (
type (
composePage struct {
res *types.Page
ts *resource.Timestamps
children composePageSet
refNamespace string
@@ -109,6 +110,10 @@ func (wrap *composePage) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
return eachMap(n, func(k, v *yaml.Node) (err error) {
switch k.Value {
case "title":
@@ -149,6 +154,7 @@ func (wrap *composePage) UnmarshalYAML(n *yaml.Node) (err error) {
func (wrap composePage) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewComposePage(wrap.res, wrap.refNamespace, wrap.refModule, wrap.refParent)
rs.SetTimestamps(wrap.ts)
return envoy.CollectNodes(
rs,

View File

@@ -10,15 +10,14 @@ import (
type (
composeRecord struct {
values map[string]string
sysValues map[string]string
values map[string]string
ts *resource.Timestamps
us *resource.Userstamps
eCfg *resource.EnvoyConfig
refModule string
refNamespace string
// createdBy, updatedBy, deletedBy, ownedBy
refUser map[string]string
}
composeRecordSet []*composeRecord
@@ -77,9 +76,10 @@ func (wset composeRecordSet) MarshalEnvoy() ([]resource.Interface, error) {
type (
rw struct {
rr resource.ComposeRecordRawSet
nsRef string
modRef string
rr resource.ComposeRecordRawSet
nsRef string
modRef string
refUser resource.Identifiers
}
)
@@ -89,9 +89,10 @@ func (wset composeRecordSet) MarshalEnvoy() ([]resource.Interface, error) {
for _, res := range wset {
if recMap[res.refModule] == nil {
recMap[res.refModule] = &rw{
rr: make(resource.ComposeRecordRawSet, 0, 10),
nsRef: res.refNamespace,
modRef: res.refModule,
rr: make(resource.ComposeRecordRawSet, 0, 10),
nsRef: res.refNamespace,
modRef: res.refModule,
refUser: make(resource.Identifiers),
}
}
@@ -99,9 +100,17 @@ func (wset composeRecordSet) MarshalEnvoy() ([]resource.Interface, error) {
// @todo change this probably
ID: res.values["id"],
Config: res.eCfg,
Values: res.values,
Ts: res.ts,
Us: res.us,
}
r.ApplyValues(res.values)
r.ApplyValues(res.sysValues)
recMap[res.refModule].refUser.Add(
res.us.CreatedBy,
res.us.UpdatedBy,
res.us.DeletedBy,
res.us.OwnedBy,
)
recMap[res.refModule].rr = append(recMap[res.refModule].rr, r)
}
@@ -118,6 +127,7 @@ func (wset composeRecordSet) MarshalEnvoy() ([]resource.Interface, error) {
}
n := resource.NewComposeRecordSet(walker, w.nsRef, w.modRef)
n.SetUserRefs(w.refUser.StringSlice())
for _, r := range w.rr {
n.IDMap[r.ID] = 0
}
@@ -141,15 +151,9 @@ func (wset composeRecordSet) setNamespaceRef(ref string) error {
}
func (wrap *composeRecord) UnmarshalYAML(n *yaml.Node) (err error) {
if wrap.refUser == nil {
wrap.refUser = make(map[string]string)
}
if wrap.values == nil {
wrap.values = make(map[string]string)
}
if wrap.sysValues == nil {
wrap.sysValues = make(map[string]string)
}
// @todo enable when records are ready for RBAC
//if wrap.rbac, err = decodeRbac(types.RecordRBACResource, n); err != nil {
@@ -160,6 +164,13 @@ func (wrap *composeRecord) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
if wrap.us, err = decodeUserstamps(n); err != nil {
return
}
return eachMap(n, func(k, v *yaml.Node) error {
switch k.Value {
case "module":
@@ -172,28 +183,6 @@ func (wrap *composeRecord) UnmarshalYAML(n *yaml.Node) (err error) {
}
return nil
case "createdAt":
wrap.sysValues["createdAt"] = v.Value
return nil
case "updatedAt":
wrap.sysValues["updatedAt"] = v.Value
return nil
case "deletedAt":
wrap.sysValues["deletedAt"] = v.Value
return nil
case "createdBy":
wrap.refUser["createdBy"] = v.Value
return nil
case "updatedBy":
wrap.refUser["updatedBy"] = v.Value
return nil
case "deletedBy":
wrap.refUser["deletedBy"] = v.Value
return nil
case "ownedBy":
wrap.refUser["ownedBy"] = v.Value
return nil
}
return nil

View File

@@ -11,9 +11,7 @@ func TestComposeRecord_UnmarshalYAML(t *testing.T) {
var (
parseString = func(src string) (*composeRecord, error) {
w := &composeRecord{
values: make(map[string]string),
sysValues: make(map[string]string),
refUser: make(map[string]string),
values: make(map[string]string),
}
return w, yaml.Unmarshal([]byte(src), w)
}
@@ -35,7 +33,8 @@ func TestComposeRecord_UnmarshalYAML(t *testing.T) {
req.NoError(err)
req.NotNil(w)
req.NotEmpty(w.values)
req.NotEmpty(w.sysValues)
req.NotNil(w.ts)
req.NotNil(w.us)
req.Equal("bar", w.values["foo"])
})

View File

@@ -15,7 +15,7 @@ type (
roles roleSet
users userSet
applications applicationSet
settings settings
settings *settings
rbac rbacRuleSet
}
)

View File

@@ -11,6 +11,8 @@ type (
messagingChannel struct {
// when messagingChannel is at least partially defined
res *types.Channel `yaml:",inline"`
ts *resource.Timestamps
us *resource.Userstamps
// module's RBAC rules
rbac rbacRuleSet
@@ -35,7 +37,7 @@ func (wset *messagingChannelSet) UnmarshalYAML(n *yaml.Node) error {
}
wrap.res = &types.Channel{}
if err = v.Decode(&wrap.res); err != nil {
if err = v.Decode(&wrap); err != nil {
return
}
@@ -76,11 +78,20 @@ func (wrap *messagingChannel) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
if wrap.us, err = decodeUserstamps(n); err != nil {
return
}
return nil
}
func (wrap messagingChannel) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewMessagingChannel(wrap.res)
rs.SetTimestamps(wrap.ts)
rs.SetUserstamps(wrap.us)
return envoy.CollectNodes(
rs,
wrap.rbac.bindResource(rs),

View File

@@ -11,6 +11,7 @@ type (
role struct {
// when role is at least partially defined
res *types.Role
ts *resource.Timestamps
// all known modules on a role
modules composeModuleSet
@@ -47,7 +48,7 @@ func (wset *roleSet) UnmarshalYAML(n *yaml.Node) error {
}
case yaml.MappingNode:
if err = v.Decode(&wrap.res); err != nil {
if err = v.Decode(&wrap); err != nil {
return
}
}
@@ -93,11 +94,17 @@ func (wrap *role) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
return nil
}
func (wrap role) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewRole(wrap.res)
rs.SetTimestamps(wrap.ts)
return envoy.CollectNodes(
rs,
wrap.rbac.bindResource(rs),

View File

@@ -2,14 +2,43 @@ package yaml
import (
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
"gopkg.in/yaml.v3"
)
type (
settings map[string]interface{}
settings struct {
res map[string]interface{}
ts *resource.Timestamps
us *resource.Userstamps
}
)
func (wrap *settings) UnmarshalYAML(n *yaml.Node) (err error) {
if !isKind(n, yaml.MappingNode) {
return nodeErr(n, "role definition must be a map")
}
if wrap.res == nil {
wrap.res = make(map[string]interface{})
}
if err = n.Decode(&wrap.res); err != nil {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
if wrap.us, err = decodeUserstamps(n); err != nil {
return
}
return nil
}
func (wrap settings) MarshalEnvoy() (nn []resource.Interface, err error) {
n := resource.NewSettings(wrap)
n := resource.NewSettings(wrap.res)
n.SetTimestamps(wrap.ts)
n.SetUserstamps(wrap.us)
return []resource.Interface{n}, nil
}

View File

@@ -13,8 +13,8 @@ func TestSettings_UnmarshalYAML(t *testing.T) {
doc, err := parseDocument("settings_1")
req.NoError(err)
req.NotNil(doc)
req.Len(doc.settings, 20)
req.Contains(doc.settings, "privacy.mask.email")
req.Equal(true, doc.settings["privacy.mask.email"])
req.Len(doc.settings.res, 20)
req.Contains(doc.settings.res, "privacy.mask.email")
req.Equal(true, doc.settings.res["privacy.mask.email"])
})
}

51
pkg/envoy/yaml/stamps.go Normal file
View File

@@ -0,0 +1,51 @@
package yaml
import (
"strings"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
"gopkg.in/yaml.v3"
)
func decodeTimestamps(n *yaml.Node) (*resource.Timestamps, error) {
var (
st = &resource.Timestamps{}
)
return st, eachMap(n, func(k, v *yaml.Node) (err error) {
switch strings.ToLower(k.Value) {
case "createdat":
return decodeScalar(v, "created at", &st.CreatedAt)
case "updatedat":
return decodeScalar(v, "updated at", &st.UpdatedAt)
case "deletedat":
return decodeScalar(v, "deleted at", &st.DeletedAt)
case "suspendedat":
return decodeScalar(v, "suspended at", &st.SuspendedAt)
case "archivedat":
return decodeScalar(v, "archived at", &st.ArchivedAt)
}
return nil
})
}
func decodeUserstamps(n *yaml.Node) (*resource.Userstamps, error) {
var (
us = &resource.Userstamps{}
)
return us, eachMap(n, func(k, v *yaml.Node) (err error) {
switch strings.ToLower(k.Value) {
case "createdby",
"creatorid":
return decodeScalar(v, "created by", &us.CreatedBy)
case "updatedby":
return decodeScalar(v, "updated by", &us.UpdatedBy)
case "deletedby":
return decodeScalar(v, "deleted by", &us.DeletedBy)
case "ownedby":
return decodeScalar(v, "owned by", &us.OwnedBy)
}
return nil
})
}

View File

@@ -11,6 +11,7 @@ type (
user struct {
// when user is at least partially defined
res *types.User `yaml:",inline"`
ts *resource.Timestamps
// module's RBAC rules
rbac rbacRuleSet
@@ -44,7 +45,7 @@ func (wset *userSet) UnmarshalYAML(n *yaml.Node) error {
}
case yaml.MappingNode:
if err = v.Decode(&wrap.res); err != nil {
if err = v.Decode(&wrap); err != nil {
return
}
@@ -94,11 +95,17 @@ func (wrap *user) UnmarshalYAML(n *yaml.Node) (err error) {
return
}
if wrap.ts, err = decodeTimestamps(n); err != nil {
return
}
return nil
}
func (wrap user) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewUser(wrap.res)
rs.SetTimestamps(wrap.ts)
return envoy.CollectNodes(
rs,
wrap.rbac.bindResource(rs),