snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
package resource
import (
"fmt"
"strconv"
"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 {
// @todo we could use method + path combo to uniquely identify them also
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) Resource() interface{} {
return r.Res
}
func (r *APIGateway) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.ApigwRouteRbacResourceTpl(), types.ApigwRouteResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10)))
return
}
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())
}

View File

@@ -0,0 +1,80 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
// Application represents a Application
Application struct {
*base
Res *types.Application
}
)
func NewApplication(res *types.Application) *Application {
r := &Application{base: &base{}}
r.SetResourceType(types.ApplicationResourceType)
r.Res = res
r.AddIdentifier(identifiers(res.Name, res.ID)...)
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
// Initial userstamps
if res.OwnerID > 0 {
r.SetUserstamps(&Userstamps{
OwnedBy: &Userstamp{UserID: res.OwnerID},
})
}
return r
}
func (r *Application) Resource() interface{} {
return r.Res
}
func (r *Application) SysID() uint64 {
return r.Res.ID
}
func (r *Application) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.ApplicationRbacResourceTpl(), types.ApplicationResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10)))
return
}
// FindApplication looks for the app in the resource set
func FindApplication(rr InterfaceSet, ii Identifiers) (ap *types.Application) {
var apRes *Application
rr.Walk(func(r Interface) error {
ar, ok := r.(*Application)
if !ok {
return nil
}
if ar.Identifiers().HasAny(ii) {
apRes = ar
}
return nil
})
// Found it
if apRes != nil {
return apRes.Res
}
return nil
}
func ApplicationErrUnresolved(ii Identifiers) error {
return fmt.Errorf("application unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,155 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/automation/types"
)
type (
AutomationWorkflow struct {
*base
Res *types.Workflow
Triggers []*AutomationTrigger
Steps []*AutomationWorkflowStep
Paths []*AutomationWorkflowPath
}
AutomationTrigger struct {
*base
Res *types.Trigger
}
AutomationWorkflowStep struct {
*base
Res *types.WorkflowStep
}
AutomationWorkflowPath struct {
*base
Res *types.WorkflowPath
ParentStep Identifiers
ChildStep Identifiers
}
)
func NewAutomationWorkflow(res *types.Workflow) *AutomationWorkflow {
r := &AutomationWorkflow{
base: &base{},
}
r.SetResourceType(types.WorkflowResourceType)
r.Res = res
r.AddIdentifier(identifiers(res.Handle, res.Meta.Name, res.ID)...)
// Initial stamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
us := MakeUserstampsCUDO(res.CreatedBy, res.UpdatedBy, res.DeletedBy, res.OwnedBy)
us.RunAs = MakeUserstampFromID(res.RunAs)
r.SetUserstamps(us)
return r
}
func (r *AutomationWorkflow) Resource() interface{} {
return r.Res
}
func (r *AutomationWorkflow) AddAutomationTrigger(res *types.Trigger) *AutomationTrigger {
t := &AutomationTrigger{
base: &base{},
}
t.Res = res
// Initial stamps
t.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
t.SetUserstamps(MakeUserstampsCUDO(res.CreatedBy, res.UpdatedBy, res.DeletedBy, res.OwnedBy))
if r.Triggers == nil {
r.Triggers = make([]*AutomationTrigger, 0, 2)
}
r.Triggers = append(r.Triggers, t)
return t
}
func (r *AutomationWorkflow) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.WorkflowRbacResourceTpl(), types.WorkflowResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *AutomationWorkflow) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.WorkflowResourceTranslationTpl(), types.WorkflowResourceTranslationType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *AutomationWorkflow) AddAutomationWorkflowStep(res *types.WorkflowStep) *AutomationWorkflowStep {
s := &AutomationWorkflowStep{
base: &base{},
}
s.Res = res
if r.Steps == nil {
r.Steps = make([]*AutomationWorkflowStep, 0, 100)
}
r.Steps = append(r.Steps, s)
return s
}
func (r *AutomationWorkflow) AddAutomationWorkflowPath(res *types.WorkflowPath) *AutomationWorkflowPath {
p := &AutomationWorkflowPath{
base: &base{},
}
p.Res = res
if r.Paths == nil {
r.Paths = make([]*AutomationWorkflowPath, 0, 100)
}
r.Paths = append(r.Paths, p)
return p
}
func (r *AutomationWorkflow) SysID() uint64 {
return r.Res.ID
}
// FindAutomationWorkflow looks for the workflow in the resource set
func FindAutomationWorkflow(rr InterfaceSet, ii Identifiers) (ns *types.Workflow) {
var wfRes *AutomationWorkflow
rr.Walk(func(r Interface) error {
wr, ok := r.(*AutomationWorkflow)
if !ok {
return nil
}
if wr.Identifiers().HasAny(ii) {
wfRes = wr
}
return nil
})
// Found it
if wfRes != nil {
return wfRes.Res
}
return nil
}
func AutomationWorkflowErrUnresolved(ii Identifiers) error {
return fmt.Errorf("automation workflow unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,114 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/compose/types"
)
type (
// ComposeChart represents a ComposeChart
ComposeChart struct {
*base
Res *types.Chart
// Might keep track of related namespace
RefNs *Ref
RefMods RefSet
}
)
func NewComposeChart(res *types.Chart, nsRef *Ref, mmRef RefSet) *ComposeChart {
r := &ComposeChart{
base: &base{},
RefMods: make(RefSet, len(mmRef)),
}
r.SetResourceType(types.ChartResourceType)
r.Res = res
r.AddIdentifier(identifiers(res.Handle, res.Name, res.ID)...)
r.RefNs = r.addRef(nsRef)
for i, mRef := range mmRef {
r.RefMods[i] = r.addRef(mRef).Constraint(r.RefNs)
}
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
return r
}
func (r *ComposeChart) Resource() interface{} {
return r.Res
}
func (r *ComposeChart) ReRef(old RefSet, new RefSet) {
r.base.ReRef(old, new)
// Additional references
for _, n := range new {
if n.ResourceType == types.NamespaceResourceType {
r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers)
}
}
for i, o := range old {
if o.ResourceType == types.ModuleResourceType {
r.RefMods = r.RefMods.replaceRef(o, new[i])
}
}
}
func (r *ComposeChart) SysID() uint64 {
return r.Res.ID
}
func (r *ComposeChart) resourceParts(tpl string) (resource string, ref *Ref, path []*Ref) {
ref = r.Ref().Constraint(r.RefNs)
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.ChartRbacResourceTpl(), types.ChartResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *ComposeChart) RBACParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ChartRbacResourceTpl())
}
func (r *ComposeChart) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ChartResourceTranslationTpl())
}
func (r *ComposeChart) encodeTranslations() ([]*ResourceTranslation, error) {
return nil, nil
}
// FindComposeChart looks for the chart in the resources
func FindComposeChart(rr InterfaceSet, ii Identifiers) (ch *types.Chart) {
var chRes *ComposeChart
rr.Walk(func(r Interface) error {
cr, ok := r.(*ComposeChart)
if !ok {
return nil
}
if cr.Identifiers().HasAny(ii) {
chRes = cr
}
return nil
})
// Found it
if chRes != nil {
return chRes.Res
}
return nil
}
func ComposeChartErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose chart unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,220 @@
package resource
import (
"fmt"
"strconv"
automationTypes "github.com/cortezaproject/corteza/server/automation/types"
"github.com/cortezaproject/corteza/server/compose/types"
)
type (
// ComposePageLayout represents a ComposePageLayout
ComposePageLayout struct {
*base
Res *types.PageLayout
RefNs *Ref
RefMod *Ref
RefParent *Ref
WfRefs RefSet
ModRefs RefSet
RefCharts RefSet
BlockRefs map[int]RefSet
}
)
func NewComposePageLayout(pg *types.PageLayout, nsRef, modRef, parentRef *Ref) *ComposePageLayout {
r := &ComposePageLayout{
base: &base{},
WfRefs: make(RefSet, 0, 10),
ModRefs: make(RefSet, 0, 10),
RefCharts: make(RefSet, 0, 10),
BlockRefs: make(map[int]RefSet),
}
r.SetResourceType(types.PageLayoutResourceType)
r.Res = pg
r.AddIdentifier(identifiers(pg.Handle, pg.Meta.Title, pg.ID)...)
r.RefNs = r.addRef(nsRef)
if modRef != nil {
r.RefMod = r.addRef(modRef).Constraint(r.RefNs)
}
if parentRef != nil {
r.RefParent = r.addRef(parentRef).Constraint(r.RefNs)
}
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&pg.CreatedAt, pg.UpdatedAt, pg.DeletedAt, nil))
return r
}
func UnpackComposePageLayout(p *types.PageLayout) (*types.PageLayout, *Ref, *Ref) {
var modRef *Ref
var parentRef *Ref
return p, modRef, parentRef
}
func (r *ComposePageLayout) Resource() interface{} {
return r.Res
}
func (r *ComposePageLayout) ReRef(old RefSet, new RefSet) {
r.base.ReRef(old, new)
for _, n := range new {
switch n.ResourceType {
case types.NamespaceResourceType:
r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers)
case types.ModuleResourceType:
r.RefMod = MakeRef(types.ModuleResourceType, n.Identifiers)
case types.PageLayoutResourceType:
r.RefParent = MakeRef(types.PageLayoutResourceType, n.Identifiers)
}
}
}
func (r *ComposePageLayout) Prune(ref *Ref) {
}
func (r *ComposePageLayout) SysID() uint64 {
return r.Res.ID
}
func (r *ComposePageLayout) resourceParts(tpl string) (resource string, ref *Ref, path []*Ref) {
ref = r.Ref().Constraint(r.RefNs)
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.PageLayoutRbacResourceTpl(), types.PageLayoutResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *ComposePageLayout) RBACParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.PageLayoutRbacResourceTpl())
}
func (r *ComposePageLayout) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.PageLayoutResourceTranslationTpl())
}
func (r *ComposePageLayout) encodeTranslations() ([]*ResourceTranslation, error) {
return nil, nil
}
// FindComposePageLayout looks for the page in the resources
func FindComposePageLayout(rr InterfaceSet, ii Identifiers) (pg *types.PageLayout) {
var pgRes *ComposePageLayout
rr.Walk(func(r Interface) error {
pr, ok := r.(*ComposePageLayout)
if !ok {
return nil
}
if pr.Identifiers().HasAny(ii) {
pgRes = pr
}
return nil
})
// Found it
if pgRes != nil {
return pgRes.Res
}
return nil
}
func ComposePageLayoutErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose page unresolved %v", ii.StringSlice())
}
// PageLayout block utilities
func (r *ComposePageLayout) removeBlock(i int) {
// do the swap remove thing
r.Res.Blocks[i] = r.Res.Blocks[len(r.Res.Blocks)-1]
r.Res.Blocks = r.Res.Blocks[:len(r.Res.Blocks)-1]
// correct block refs.
// +1 here because it's already cut
r.BlockRefs[i] = r.BlockRefs[len(r.Res.Blocks)]
delete(r.BlockRefs, len(r.Res.Blocks))
}
func (r *ComposePageLayout) optString(opt map[string]interface{}, kk ...string) string {
for _, k := range kk {
if vr, has := opt[k]; has {
v, _ := vr.(string)
return v
}
}
return ""
}
func (r *ComposePageLayout) pbRecordList(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePageLayout) pbComment(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePageLayout) pbAutomation(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "workflow", "workflowID")
if id == "" || id == "0" {
return
}
return MakeRef(automationTypes.WorkflowResourceType, MakeIdentifiers(id))
}
func (r *ComposePageLayout) pbRecordOrganizer(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePageLayout) pbChart(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "chart", "chartID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ChartResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePageLayout) pbCalendar(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePageLayout) pbMetric(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}

View File

@@ -0,0 +1,308 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/compose/types"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/modern-go/reflect2"
"github.com/spf13/cast"
)
type (
ComposeModuleField struct {
*base
Res *types.ModuleField
RefNs *Ref
RefMod *Ref
}
ComposeModule struct {
*base
Res *types.Module
ResFields []*ComposeModuleField
// Might keep track of related NS
RefNs *Ref
RefMods RefSet
RefRoles RefSet
}
)
func NewComposeModule(res *types.Module, nsRef *Ref) *ComposeModule {
r := &ComposeModule{
base: &base{},
RefMods: make(RefSet, 0, len(res.Fields)),
}
r.SetResourceType(types.ModuleResourceType)
r.Res = res
r.AddIdentifier(identifiers(res.Handle, res.Name, res.ID)...)
r.RefNs = r.addRef(nsRef)
// Field deps
for _, f := range res.Fields {
switch f.Kind {
case "Record":
refMod := f.Options.String("module")
if refMod == "" {
refMod = f.Options.String("moduleID")
}
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))
}
}
}
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
return r
}
func (r *ComposeModule) Resource() interface{} {
return r.Res
}
func (r *ComposeModule) ReRef(old RefSet, new RefSet) {
r.base.ReRef(old, new)
// Additional references
for _, n := range new {
if n.ResourceType == types.NamespaceResourceType {
r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers)
}
}
for i, o := range old {
if o.ResourceType == types.ModuleResourceType {
r.RefMods = r.RefMods.replaceRef(o, new[i])
}
}
}
func (r *ComposeModule) SysID() uint64 {
return r.Res.ID
}
func (r *ComposeModule) resourceParts(tpl string) (resource string, ref *Ref, path []*Ref) {
ref = r.Ref().Constraint(r.RefNs)
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(tpl, types.ModuleResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *ComposeModule) RBACParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ModuleRbacResourceTpl())
}
func (r *ComposeModule) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ModuleResourceTranslationTpl())
}
func (r *ComposeModule) encodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, len(r.ResFields))
for _, f := range r.ResFields {
rr := f.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := f.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
}
return out, nil
}
// FindComposeModule looks for the module in the resource set
func FindComposeModule(rr InterfaceSet, ii Identifiers) (ns *types.Module) {
var modRes *ComposeModule
rr.Walk(func(r Interface) error {
mr, ok := r.(*ComposeModule)
if !ok {
return nil
}
if mr.Identifiers().HasAny(ii) {
modRes = mr
}
return nil
})
// Found it
if modRes != nil {
return modRes.Res
}
return nil
}
func (r *ComposeModule) AddField(f *ComposeModuleField) {
r.ResFields = append(r.ResFields, f)
}
// FindComposeModule looks for the module in the resource set
func FindComposeModuleField(rr InterfaceSet, mod, ii Identifiers) (field *types.ModuleField) {
var fieldRes *ComposeModuleField
rr.Walk(func(r Interface) error {
mr, ok := r.(*ComposeModule)
if !ok {
return nil
}
if !mr.Identifiers().HasAny(mod) {
return nil
}
for _, rf := range mr.ResFields {
if rf.Identifiers().HasAny(ii) {
fieldRes = rf
}
}
return nil
})
// Found it
if fieldRes != nil {
return fieldRes.Res
}
return nil
}
func (r *ComposeModuleField) resourceParts(tpl string) (resource string, ref *Ref, path []*Ref) {
ref = r.Ref().Constraint(r.RefNs).Constraint(r.RefMod)
path = []*Ref{r.RefNs, r.RefMod}
resource = fmt.Sprintf(types.ModuleFieldResourceTranslationTpl(), types.ModuleFieldResourceTranslationType, r.RefNs.Identifiers.First(), r.RefMod.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Name))
return
}
func (r *ComposeModuleField) RBACParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ModuleFieldRbacResourceTpl())
}
func (r *ComposeModuleField) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ModuleFieldResourceTranslationTpl())
}
func (r *ComposeModuleField) ResourceTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
return out, nil
}
func ComposeModuleErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose module unresolved %v", ii.StringSlice())
}
func ComposeModuleFieldErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose module field unresolved %v", ii.StringSlice())
}
func NewComposeModuleField(res *types.ModuleField, nsRef, modRef *Ref) *ComposeModuleField {
r := &ComposeModuleField{
base: &base{},
}
r.SetResourceType(types.ModuleFieldResourceType)
r.Res = res
r.AddIdentifier(identifiers(res.Name, res.ID)...)
r.RefNs = r.addRef(nsRef)
r.RefMod = r.addRef(modRef).Constraint(r.RefNs)
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
return r
}
func (r *ComposeModuleField) Resource() interface{} {
return r.Res
}
// ComposeModuleFieldExtractUserFieldRoles is a helper to extract roles
// from the given filer options.
func ComposeModuleFieldExtractUserFieldRoles(i interface{}) []string {
if reflect2.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
}

View File

@@ -0,0 +1,80 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/compose/types"
)
type (
ComposeNamespace struct {
*base
Res *types.Namespace
}
)
func NewComposeNamespace(ns *types.Namespace) *ComposeNamespace {
r := &ComposeNamespace{base: &base{}}
r.SetResourceType(types.NamespaceResourceType)
r.Res = ns
r.AddIdentifier(identifiers(ns.Slug, ns.Name, ns.ID)...)
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&ns.CreatedAt, ns.UpdatedAt, ns.DeletedAt, nil))
return r
}
func (r *ComposeNamespace) Resource() interface{} {
return r.Res
}
func (r *ComposeNamespace) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.NamespaceRbacResourceTpl(), types.NamespaceResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Slug))
return
}
func (r *ComposeNamespace) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.NamespaceResourceTranslationTpl(), types.NamespaceResourceTranslationType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Slug))
return
}
func (r *ComposeNamespace) SysID() uint64 {
return r.Res.ID
}
// FindComposeNamespace looks for the namespace in the resource set
func FindComposeNamespace(rr InterfaceSet, ii Identifiers) (ns *types.Namespace) {
var nsRes *ComposeNamespace
rr.Walk(func(r Interface) error {
nr, ok := r.(*ComposeNamespace)
if !ok {
return nil
}
if nr.Identifiers().HasAny(ii) {
nsRes = nr
}
return nil
})
// Found it
if nsRes != nil {
return nsRes.Res
}
return nil
}
func ComposeNamespaceErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose namespace unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,340 @@
package resource
import (
"fmt"
"strconv"
automationTypes "github.com/cortezaproject/corteza/server/automation/types"
"github.com/cortezaproject/corteza/server/compose/types"
)
type (
// ComposePage represents a ComposePage
ComposePage struct {
*base
Res *types.Page
RefNs *Ref
RefMod *Ref
RefParent *Ref
WfRefs RefSet
ModRefs RefSet
RefCharts RefSet
BlockRefs map[int]RefSet
}
)
func NewComposePage(pg *types.Page, nsRef, modRef, parentRef *Ref) *ComposePage {
r := &ComposePage{
base: &base{},
WfRefs: make(RefSet, 0, 10),
ModRefs: make(RefSet, 0, 10),
RefCharts: make(RefSet, 0, 10),
BlockRefs: make(map[int]RefSet),
}
r.SetResourceType(types.PageResourceType)
r.Res = pg
r.AddIdentifier(identifiers(pg.Handle, pg.Title, pg.ID)...)
r.RefNs = r.addRef(nsRef)
if modRef != nil {
r.RefMod = r.addRef(modRef).Constraint(r.RefNs)
}
if parentRef != nil {
r.RefParent = r.addRef(parentRef).Constraint(r.RefNs)
}
var ref *Ref
for i, b := range pg.Blocks {
switch b.Kind {
case "RecordList":
ref = r.pbRecordList(b.Options)
if ref != nil {
r.addRef(ref)
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.ModRefs = append(r.ModRefs, ref)
}
case "Automation":
bb, _ := b.Options["buttons"].([]interface{})
for _, b := range bb {
button, _ := b.(map[string]interface{})
ref = r.pbAutomation(button)
if ref != nil {
r.addRef(ref)
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.WfRefs = append(r.WfRefs, ref)
}
}
case "RecordOrganizer":
ref = r.pbRecordList(b.Options)
if ref != nil {
r.addRef(ref)
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.ModRefs = append(r.ModRefs, ref)
}
case "Chart":
ref = r.pbChart(b.Options)
if ref != nil {
r.addRef(ref)
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.RefCharts = append(r.RefCharts, ref)
}
case "Calendar":
ff, _ := b.Options["feeds"].([]interface{})
for _, f := range ff {
feed, _ := f.(map[string]interface{})
fOpts, _ := (feed["options"]).(map[string]interface{})
ref = r.pbCalendar(fOpts)
if ref != nil {
r.addRef(ref)
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.ModRefs = append(r.ModRefs, ref)
}
}
case "Metric":
mm, _ := b.Options["metrics"].([]interface{})
for _, m := range mm {
mops, _ := m.(map[string]interface{})
ref = r.pbMetric(mops)
if ref != nil {
r.addRef(ref)
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.ModRefs = append(r.ModRefs, ref)
}
}
case "Comment":
ref = r.pbComment(b.Options)
if ref != nil {
r.addRef(ref.Constraint(r.RefNs))
r.BlockRefs[i] = append(r.BlockRefs[i], ref)
r.ModRefs = append(r.ModRefs, ref)
}
}
}
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&pg.CreatedAt, pg.UpdatedAt, pg.DeletedAt, nil))
return r
}
func UnpackComposePage(p *types.Page) (*types.Page, *Ref, *Ref) {
var modRef *Ref
var parentRef *Ref
if p.ModuleID != 0 {
modRef = MakeModuleRef(p.ModuleID, "", "")
}
if p.SelfID != 0 {
parentRef = MakePageRef(p.SelfID, "", "")
}
return p, modRef, parentRef
}
func (r *ComposePage) Resource() interface{} {
return r.Res
}
func (r *ComposePage) ReRef(old RefSet, new RefSet) {
r.base.ReRef(old, new)
for _, n := range new {
switch n.ResourceType {
case types.NamespaceResourceType:
r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers)
case types.ModuleResourceType:
r.RefMod = MakeRef(types.ModuleResourceType, n.Identifiers)
case types.PageResourceType:
r.RefParent = MakeRef(types.PageResourceType, n.Identifiers)
}
}
}
func (r *ComposePage) Prune(ref *Ref) {
var auxRef *Ref
outer:
for i := len(r.Res.Blocks) - 1; i >= 0; i-- {
b := r.Res.Blocks[i]
switch b.Kind {
// Implement the rest when support is needed
case "Automation":
if b.Options["buttons"] == nil {
// In case the block isn't connected to a workflow (placeholder, script)
if auxRef == nil {
r.removeBlock(i)
continue outer
}
} else {
bb, _ := b.Options["buttons"].([]interface{})
for _, b := range bb {
button, _ := b.(map[string]interface{})
auxRef = r.pbAutomation(button)
// In case the block isn't connected to a workflow (placeholder, script)
if auxRef == nil {
r.removeBlock(i)
continue outer
}
// In case we are removing it
if auxRef.equals(ref) {
r.ReplaceRef(ref, nil)
r.WfRefs = r.WfRefs.replaceRef(ref, nil)
r.removeBlock(i)
continue outer
}
}
}
}
}
}
func (r *ComposePage) SysID() uint64 {
return r.Res.ID
}
func (r *ComposePage) resourceParts(tpl string) (resource string, ref *Ref, path []*Ref) {
ref = r.Ref().Constraint(r.RefNs)
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.PageRbacResourceTpl(), types.PageResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *ComposePage) RBACParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.PageRbacResourceTpl())
}
func (r *ComposePage) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.PageResourceTranslationTpl())
}
func (r *ComposePage) encodeTranslations() ([]*ResourceTranslation, error) {
return nil, nil
}
// FindComposePage looks for the page in the resources
func FindComposePage(rr InterfaceSet, ii Identifiers) (pg *types.Page) {
var pgRes *ComposePage
rr.Walk(func(r Interface) error {
pr, ok := r.(*ComposePage)
if !ok {
return nil
}
if pr.Identifiers().HasAny(ii) {
pgRes = pr
}
return nil
})
// Found it
if pgRes != nil {
return pgRes.Res
}
return nil
}
func ComposePageErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose page unresolved %v", ii.StringSlice())
}
// Page block utilities
func (r *ComposePage) removeBlock(i int) {
// do the swap remove thing
r.Res.Blocks[i] = r.Res.Blocks[len(r.Res.Blocks)-1]
r.Res.Blocks = r.Res.Blocks[:len(r.Res.Blocks)-1]
// correct block refs.
// +1 here because it's already cut
r.BlockRefs[i] = r.BlockRefs[len(r.Res.Blocks)]
delete(r.BlockRefs, len(r.Res.Blocks))
}
func (r *ComposePage) optString(opt map[string]interface{}, kk ...string) string {
for _, k := range kk {
if vr, has := opt[k]; has {
v, _ := vr.(string)
return v
}
}
return ""
}
func (r *ComposePage) pbRecordList(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePage) pbComment(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePage) pbAutomation(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "workflow", "workflowID")
if id == "" || id == "0" {
return
}
return MakeRef(automationTypes.WorkflowResourceType, MakeIdentifiers(id))
}
func (r *ComposePage) pbRecordOrganizer(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePage) pbChart(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "chart", "chartID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ChartResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePage) pbCalendar(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}
func (r *ComposePage) pbMetric(opt map[string]interface{}) (out *Ref) {
id := r.optString(opt, "module", "moduleID")
if id == "" || id == "0" {
return
}
return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs)
}

View File

@@ -0,0 +1,115 @@
package resource
import (
"fmt"
"github.com/cortezaproject/corteza/server/compose/types"
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
)
type (
rawSysValues struct {
OwnedBy string
CreatedAt string
CreatedBy string
UpdatedAt string
UpdatedBy string
DeletedAt string
DeletedBy string
}
ComposeRecordRaw struct {
ID string
Values map[string]ComposeRecordRawValue
Ts *Timestamps
Us *Userstamps
Config *EnvoyConfig
}
ComposeRecordRawSet []*ComposeRecordRaw
ComposeRecordRawValue struct {
Name string
IsMulti bool
Value string
Values []string
}
CrsWalker func(f func(r *ComposeRecordRaw) error) error
ComposeRecord struct {
*base
Walker CrsWalker
RefNs *Ref
RefMod *Ref
RelMod *composeTypes.Module
IDMap map[string]uint64
// UserFlakes help the system by predefining a set of potential sys user references.
// This should make the operation cheaper for larger datasets.
UserFlakes UserstampIndex
}
)
func NewComposeRecordSet(w CrsWalker, nsRef, modRef string) *ComposeRecord {
r := &ComposeRecord{
base: &base{},
IDMap: make(map[string]uint64),
}
r.SetResourceType(composeTypes.RecordResourceType)
r.Walker = w
r.AddIdentifier(identifiers(modRef)...)
r.RefNs = r.AddRef(composeTypes.NamespaceResourceType, nsRef)
r.RefMod = r.AddRef(composeTypes.ModuleResourceType, modRef).Constraint(r.RefNs)
return r
}
func (r *ComposeRecord) Resource() interface{} {
return nil
}
func (r *ComposeRecord) SetUserFlakes(uu UserstampIndex) {
r.UserFlakes = uu
// Set user refs as wildflag, indicating it refers to any user resource
r.AddRef(systemTypes.UserResourceType, "*")
}
func (r *ComposeRecord) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = []*Ref{r.RefNs, r.RefMod}
// @todo specific records
resource = fmt.Sprintf(types.RecordRbacResourceTpl(), types.RecordResourceType, r.RefNs.Identifiers.First(), r.RefMod.Identifiers.First(), "*")
return
}
func FindComposeRecordResource(rr InterfaceSet, ii Identifiers) (rec *ComposeRecord) {
rr.Walk(func(r Interface) error {
crr, ok := r.(*ComposeRecord)
if !ok {
return nil
}
if crr.Identifiers().HasAny(ii) {
rec = crr
}
return nil
})
return rec
}
func ComposeRecordErrUnresolved(ii Identifiers) error {
return fmt.Errorf("compose record unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,205 @@
package resource
import (
"strings"
)
type (
// A simple wrapper struct for related resources
composeRecordShaper struct{}
)
// ComposeRecordShaper initializes and returns a new compose record resource shaper
func ComposeRecordShaper() shaper {
return &composeRecordShaper{}
}
// Shape shapes ResourceDatasets based on the related compose template
//
// The first step finds the matching pair;
// The second step creates an actual resource based on the two.
func (crt *composeRecordShaper) Shape(rr []Interface) ([]Interface, error) {
// This will do for most cases
ii := make([]Interface, 0, int(len(rr)/2)+1)
for _, r := range rr {
rt, ok := r.(*ComposeRecordTemplate)
if !ok {
continue
}
rd := findResourceDataset(rr, rt.Identifiers())
if rd == nil {
return nil, genericSourceErrUnresolved(rt.Identifiers())
}
ii = append(ii, crt.toResource(rt, rd))
}
return ii, nil
}
func (r *composeRecordShaper) Resource() interface{} {
return nil
}
func (crt *composeRecordShaper) toResource(def *ComposeRecordTemplate, dt *ResourceDataset) Interface {
w := func(f func(r *ComposeRecordRaw) error) error {
if err := dt.P.Reset(); err != nil {
return err
}
for {
mr, err := dt.P.Next()
if err != nil {
return err
}
if mr == nil {
return nil
}
// Get the bits in order
vv := crt.mapValues(mr, def.FieldMap, def.Defaultable)
auxVals := make(map[string]ComposeRecordRawValue)
for k, v := range vv {
auxVals[k] = ComposeRecordRawValue{
Name: k,
Value: v,
}
}
rRaw := &ComposeRecordRaw{
ID: crt.getKey(mr, def.Key),
Values: auxVals,
}
crt.getTimestamps(rRaw)
crt.getUserstamps(rRaw)
// Process it
err = f(rRaw)
if err != nil {
return err
}
}
}
return NewComposeRecordSet(w, def.NsRef.Identifiers.First(), def.ModRef.Identifiers.First())
}
// mapValues maps original values based on the provided mapping
func (crt *composeRecordShaper) mapValues(ov map[string]string, fm MappingTplSet, defaultable bool) map[string]string {
nv := make(map[string]string)
// Mappings are provided as a slice since we'll want to do some additional conditional mappings.
// We'll make an index for a nicer lookup for now.
mx := make(map[string]*MappingTpl)
for _, m := range fm {
mx[m.Cell] = m
}
if defaultable {
for k, v := range ov {
if m, has := mx[k]; has {
if !m.IsIgnored() {
nv[m.Field] = v
}
} else {
nv[k] = v
}
}
} else {
if len(mx) > 0 {
for k, v := range ov {
if m, has := mx[k]; has && !m.IsIgnored() {
nv[m.Field] = v
}
}
} else {
for k, v := range ov {
nv[k] = v
}
}
}
return nv
}
func (crt *composeRecordShaper) getTimestamps(r *ComposeRecordRaw) {
ts := &Timestamps{}
// Provided values are already mapped
for k, v := range crt.cloneValues(r) {
switch strings.ToLower(k) {
case "createdat",
"created_at":
ts.CreatedAt = MakeTimestamp(v.Value)
delete(r.Values, k)
case "updatedat",
"updated_at":
ts.UpdatedAt = MakeTimestamp(v.Value)
delete(r.Values, k)
case "deletedat",
"deleted_at":
ts.DeletedAt = MakeTimestamp(v.Value)
delete(r.Values, k)
}
}
r.Ts = ts
}
func (crt *composeRecordShaper) getUserstamps(r *ComposeRecordRaw) {
us := &Userstamps{}
// Provided values are already mapped
for k, v := range crt.cloneValues(r) {
switch strings.ToLower(k) {
case "createdby",
"creatorid",
"creator":
us.CreatedBy = MakeUserstampFromRef(v.Value)
delete(r.Values, k)
case "updatedby":
us.UpdatedBy = MakeUserstampFromRef(v.Value)
delete(r.Values, k)
case "deletedby":
us.DeletedBy = MakeUserstampFromRef(v.Value)
delete(r.Values, k)
case "ownedby",
"ownerid",
"owner":
us.OwnedBy = MakeUserstampFromRef(v.Value)
delete(r.Values, k)
}
}
r.Us = us
}
func (crt *composeRecordShaper) cloneValues(r *ComposeRecordRaw) map[string]ComposeRecordRawValue {
rr := make(map[string]ComposeRecordRawValue)
for k, v := range r.Values {
rr[k] = ComposeRecordRawValue{
Name: v.Name,
Value: v.Value,
}
}
return rr
}
func (crt *composeRecordShaper) getKey(vv map[string]string, kk []string) (rtr string) {
if len(kk) <= 0 {
return ""
}
for _, k := range kk {
rtr += k + "."
}
// Remove the trailing dot
return vv[rtr[0:len(rtr)-1]]
}

View File

@@ -0,0 +1,77 @@
package resource
import (
"github.com/cortezaproject/corteza/server/compose/types"
)
type (
MappingTpl struct {
// Index is like Cell, but the index
Index uint
Cell string
Field string
// If specifies when the field should be mapped
// @todo
If string
// Expr specifies how to aditionally manipulate the value
// @todo
Expr string
}
MappingTplSet []*MappingTpl
ComposeRecordTemplate struct {
// We'll do this so it conforms to the resource.Interface
*base
ModRef *Ref
NsRef *Ref
// Name is the source name; topically file name
Name string
// Key determines what defines an identifier
Key []string
FieldMap MappingTplSet
Defaultable bool
}
)
// NewComposeRecordTemplate returns a record template based on the provided parameters.
//
// The template is only valid up until the resource shaping; it is not allowed after the fact.
func NewComposeRecordTemplate(modRef, nsRef, name string, defaultable bool, fieldMap MappingTplSet, key ...string) *ComposeRecordTemplate {
r := &ComposeRecordTemplate{base: &base{}}
r.Name = name
r.Key = key
r.FieldMap = fieldMap
r.Defaultable = defaultable
r.NsRef = r.AddRef(types.NamespaceResourceType, nsRef)
r.ModRef = r.AddRef(types.ModuleResourceType, modRef).Constraint(r.NsRef)
r.SetResourceType(DataSourceResourceType)
r.AddIdentifier(identifiers(name)...)
return r
}
func (r *ComposeRecordTemplate) Resource() interface{} {
return nil
}
// MapToMappingTplSet converts the given string map to a propper MappingTplSet
func MapToMappingTplSet(b map[string]string) MappingTplSet {
mp := make(MappingTplSet, 0, len(b))
for c, f := range b {
mp = append(mp, &MappingTpl{
Cell: c,
Field: f,
})
}
return mp
}
func (t *MappingTpl) IsIgnored() bool {
// @todo expand this?
return t.Field == "/"
}

View File

@@ -0,0 +1,30 @@
package resource
type (
provider interface {
Fields() []string
Count() uint64
Reset() error
Next() (map[string]string, error)
}
ResourceDataset struct {
*base
P provider
}
)
func NewResourceDataset(name string, p provider) *ResourceDataset {
r := &ResourceDataset{base: &base{}}
r.P = p
r.SetResourceType(DataSourceResourceType)
r.AddIdentifier(identifiers(name)...)
return r
}
func (r *ResourceDataset) Resource() interface{} {
return nil
}

View File

@@ -0,0 +1,24 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"github.com/cortezaproject/corteza/server/automation/types"
)
// AutomationWorkflowRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func AutomationWorkflowRbacReferences(workflow string) (res *Ref, pp []*Ref, err error) {
if workflow != "*" {
res = &Ref{ResourceType: types.WorkflowResourceType, Identifiers: MakeIdentifiers(workflow)}
}
return
}

View File

@@ -0,0 +1,129 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"github.com/cortezaproject/corteza/server/compose/types"
)
// ComposeChartRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposeChartRbacReferences(namespaceID string, chart string) (res *Ref, pp []*Ref, err error) {
if namespaceID != "*" {
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
}
if chart != "*" {
res = &Ref{ResourceType: types.ChartResourceType, Identifiers: MakeIdentifiers(chart)}
}
return
}
// ComposeModuleRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposeModuleRbacReferences(namespaceID string, module string) (res *Ref, pp []*Ref, err error) {
if namespaceID != "*" {
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
}
if module != "*" {
res = &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(module)}
}
return
}
// ComposeModuleFieldRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposeModuleFieldRbacReferences(namespaceID string, moduleID string, moduleField string) (res *Ref, pp []*Ref, err error) {
if namespaceID != "*" {
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
}
if moduleID != "*" {
pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(moduleID)})
}
if moduleField != "*" {
res = &Ref{ResourceType: types.ModuleFieldResourceType, Identifiers: MakeIdentifiers(moduleField)}
}
return
}
// ComposeNamespaceRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposeNamespaceRbacReferences(namespace string) (res *Ref, pp []*Ref, err error) {
if namespace != "*" {
res = &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}
}
return
}
// ComposePageRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposePageRbacReferences(namespaceID string, page string) (res *Ref, pp []*Ref, err error) {
if namespaceID != "*" {
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
}
if page != "*" {
res = &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(page)}
}
return
}
// ComposePageLayoutRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposePageLayoutRbacReferences(namespaceID string, pageID string, pageLayout string) (res *Ref, pp []*Ref, err error) {
if namespaceID != "*" {
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
}
if pageID != "*" {
pp = append(pp, &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(pageID)})
}
if pageLayout != "*" {
res = &Ref{ResourceType: types.PageLayoutResourceType, Identifiers: MakeIdentifiers(pageLayout)}
}
return
}
// ComposeRecordRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ComposeRecordRbacReferences(namespaceID string, moduleID string, record string) (res *Ref, pp []*Ref, err error) {
if namespaceID != "*" {
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
}
if moduleID != "*" {
pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(moduleID)})
}
if record != "*" {
res = &Ref{ResourceType: types.RecordResourceType, Identifiers: MakeIdentifiers(record)}
}
return
}

View File

@@ -0,0 +1,56 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"github.com/cortezaproject/corteza/server/federation/types"
)
// FederationNodeRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func FederationNodeRbacReferences(node string) (res *Ref, pp []*Ref, err error) {
if node != "*" {
res = &Ref{ResourceType: types.NodeResourceType, Identifiers: MakeIdentifiers(node)}
}
return
}
// FederationExposedModuleRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func FederationExposedModuleRbacReferences(nodeID string, exposedModule string) (res *Ref, pp []*Ref, err error) {
if nodeID != "*" {
pp = append(pp, &Ref{ResourceType: types.NodeResourceType, Identifiers: MakeIdentifiers(nodeID)})
}
if exposedModule != "*" {
res = &Ref{ResourceType: types.ExposedModuleResourceType, Identifiers: MakeIdentifiers(exposedModule)}
}
return
}
// FederationSharedModuleRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func FederationSharedModuleRbacReferences(nodeID string, sharedModule string) (res *Ref, pp []*Ref, err error) {
if nodeID != "*" {
pp = append(pp, &Ref{ResourceType: types.NodeResourceType, Identifiers: MakeIdentifiers(nodeID)})
}
if sharedModule != "*" {
res = &Ref{ResourceType: types.SharedModuleResourceType, Identifiers: MakeIdentifiers(sharedModule)}
}
return
}

View File

@@ -0,0 +1,141 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"github.com/cortezaproject/corteza/server/system/types"
)
// SystemApplicationRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemApplicationRbacReferences(application string) (res *Ref, pp []*Ref, err error) {
if application != "*" {
res = &Ref{ResourceType: types.ApplicationResourceType, Identifiers: MakeIdentifiers(application)}
}
return
}
// SystemApigwRouteRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemApigwRouteRbacReferences(apigwRoute string) (res *Ref, pp []*Ref, err error) {
if apigwRoute != "*" {
res = &Ref{ResourceType: types.ApigwRouteResourceType, Identifiers: MakeIdentifiers(apigwRoute)}
}
return
}
// SystemAuthClientRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemAuthClientRbacReferences(authClient string) (res *Ref, pp []*Ref, err error) {
if authClient != "*" {
res = &Ref{ResourceType: types.AuthClientResourceType, Identifiers: MakeIdentifiers(authClient)}
}
return
}
// SystemDataPrivacyRequestRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemDataPrivacyRequestRbacReferences(dataPrivacyRequest string) (res *Ref, pp []*Ref, err error) {
if dataPrivacyRequest != "*" {
res = &Ref{ResourceType: types.DataPrivacyRequestResourceType, Identifiers: MakeIdentifiers(dataPrivacyRequest)}
}
return
}
// SystemQueueRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemQueueRbacReferences(queue string) (res *Ref, pp []*Ref, err error) {
if queue != "*" {
res = &Ref{ResourceType: types.QueueResourceType, Identifiers: MakeIdentifiers(queue)}
}
return
}
// SystemReportRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemReportRbacReferences(report string) (res *Ref, pp []*Ref, err error) {
if report != "*" {
res = &Ref{ResourceType: types.ReportResourceType, Identifiers: MakeIdentifiers(report)}
}
return
}
// SystemRoleRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemRoleRbacReferences(role string) (res *Ref, pp []*Ref, err error) {
if role != "*" {
res = &Ref{ResourceType: types.RoleResourceType, Identifiers: MakeIdentifiers(role)}
}
return
}
// SystemTemplateRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemTemplateRbacReferences(template string) (res *Ref, pp []*Ref, err error) {
if template != "*" {
res = &Ref{ResourceType: types.TemplateResourceType, Identifiers: MakeIdentifiers(template)}
}
return
}
// SystemUserRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemUserRbacReferences(user string) (res *Ref, pp []*Ref, err error) {
if user != "*" {
res = &Ref{ResourceType: types.UserResourceType, Identifiers: MakeIdentifiers(user)}
}
return
}
// SystemDalConnectionRbacReferences generates RBAC references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func SystemDalConnectionRbacReferences(dalConnection string) (res *Ref, pp []*Ref, err error) {
if dalConnection != "*" {
res = &Ref{ResourceType: types.DalConnectionResourceType, Identifiers: MakeIdentifiers(dalConnection)}
}
return
}

View File

@@ -0,0 +1,122 @@
package resource
import (
"fmt"
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/rbac"
)
type (
RbacRule struct {
*base
Res *rbac.Rule
RefResource string
RefRes *Ref
RefRole *Ref
RefPath []*Ref
}
)
func NewRbacRule(res *rbac.Rule, refRole, refRes *Ref, refResource string, refPath ...*Ref) *RbacRule {
r := &RbacRule{base: &base{}}
r.SetResourceType(RbacResourceType)
r.Res = res
r.RefRole = r.addRef(refRole)
r.RefResource = refResource
if refRes != nil {
r.RefRes = r.addRef(refRes)
}
// any additional constraints
for i, rp := range refPath {
ref := MakeRef(rp.ResourceType, rp.Identifiers)
if r.RefRes != nil {
r.RefRes.Constraint(ref)
}
if i > 0 {
for j := i - 1; j < i; j++ {
aux := refPath[j]
ref = ref.Constraint(MakeRef(aux.ResourceType, aux.Identifiers))
}
}
r.RefPath = append(r.RefPath, r.addRef(ref))
}
// Handle cases for nested resources
if refRes != nil {
switch refRes.ResourceType {
case composeTypes.RecordResourceType:
r.handleComposeRecord(res, refPath)
case composeTypes.ModuleFieldResourceType:
r.handleComposeModuleField(res, refPath)
}
}
return r
}
func (r *RbacRule) WrapError(act string, err error) error {
return fmt.Errorf("%s %s %v: %s",
act,
r.ResourceType(),
fmt.Sprintf("{ role: %s, resource: %s, operation: %s, access: %s }", r.RefRole.Identifiers.First(), r.RefResource, r.Res.Operation, r.Res.Access),
err,
)
}
func (r *RbacRule) Resource() interface{} {
return r.Res
}
func (r *RbacRule) ReRef(old RefSet, new RefSet) {
r.base.ReRef(old, new)
// Care for wildcards
if r.RefRes != nil {
for i, o := range old {
if o.equals(r.RefRes) {
r.RefRes = new[i]
break
}
}
}
for i, o := range old {
if RefSet(r.RefPath).findRef(o) > -1 {
r.RefPath = RefSet(r.RefPath).replaceRef(o, new[i])
}
}
}
func (r *RbacRule) handleComposeRecord(res *rbac.Rule, refPath []*Ref) {
// records are grouped under module
if len(refPath) < 2 {
return
}
r.AddRef(composeTypes.RecordResourceType, refPath[1].Identifiers.StringSlice()...)
}
func (r *RbacRule) handleComposeModuleField(res *rbac.Rule, refPath []*Ref) {
// module fields are grouped under module
if len(refPath) < 2 {
return
}
r.AddRef(composeTypes.ModuleFieldResourceTranslationType, refPath[1].Identifiers.StringSlice()...)
}
func RbacResourceErrNotFound(ii Identifiers) error {
return fmt.Errorf("rbac resource not found %v", ii.StringSlice())
}

View File

@@ -0,0 +1,282 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"fmt"
automationTypes "github.com/cortezaproject/corteza/server/automation/types"
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
federationTypes "github.com/cortezaproject/corteza/server/federation/types"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"strings"
)
// Parse generates resource setting logic for each resource
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ParseRule(res string) (string, *Ref, []*Ref, error) {
if res == "" {
return "", nil, nil, fmt.Errorf("empty resource")
}
sp := "/"
res = strings.TrimSpace(res)
res = strings.TrimRight(res, sp)
rr := strings.Split(res, sp)
// only service defined (corteza::system, corteza::compose, ...)
if len(rr) == 1 {
return res, nil, nil, nil
}
// full thing
resourceType, path := rr[0], rr[1:]
for p := 1; p < len(path); p++ {
if path[p] != "*" && path[p-1] == "*" {
return "", nil, nil, fmt.Errorf("invalid path wildcard combination for '%s'", res)
}
}
// make the resource provide the slice of parent resources we should nest under
switch resourceType {
case systemTypes.ApplicationResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemApplicationRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.ApigwRouteResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemApigwRouteRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.AuthClientResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemAuthClientRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.DataPrivacyRequestResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemDataPrivacyRequestRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.QueueResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemQueueRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.ReportResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemReportRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.RoleResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemRoleRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.TemplateResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemTemplateRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.UserResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemUserRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case systemTypes.DalConnectionResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := SystemDalConnectionRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case composeTypes.ChartResourceType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeChartRbacReferences(
path[0],
path[1],
)
return resourceType, ref, pp, err
case composeTypes.ModuleResourceType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeModuleRbacReferences(
path[0],
path[1],
)
return resourceType, ref, pp, err
case composeTypes.ModuleFieldResourceType:
if len(path) != 3 {
return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeModuleFieldRbacReferences(
path[0],
path[1],
path[2],
)
return resourceType, ref, pp, err
case composeTypes.NamespaceResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeNamespaceRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case composeTypes.PageResourceType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposePageRbacReferences(
path[0],
path[1],
)
return resourceType, ref, pp, err
case composeTypes.PageLayoutResourceType:
if len(path) != 3 {
return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path))
}
ref, pp, err := ComposePageLayoutRbacReferences(
path[0],
path[1],
path[2],
)
return resourceType, ref, pp, err
case composeTypes.RecordResourceType:
if len(path) != 3 {
return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeRecordRbacReferences(
path[0],
path[1],
path[2],
)
return resourceType, ref, pp, err
case automationTypes.WorkflowResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := AutomationWorkflowRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case federationTypes.NodeResourceType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := FederationNodeRbacReferences(
path[0],
)
return resourceType, ref, pp, err
case federationTypes.ExposedModuleResourceType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := FederationExposedModuleRbacReferences(
path[0],
path[1],
)
return resourceType, ref, pp, err
case federationTypes.SharedModuleResourceType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := FederationSharedModuleRbacReferences(
path[0],
path[1],
)
return resourceType, ref, pp, err
case systemTypes.ComponentResourceType:
if len(path) != 0 {
return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path))
}
// Component resource, no path
return resourceType, nil, nil, nil
case composeTypes.ComponentResourceType:
if len(path) != 0 {
return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path))
}
// Component resource, no path
return resourceType, nil, nil, nil
case automationTypes.ComponentResourceType:
if len(path) != 0 {
return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path))
}
// Component resource, no path
return resourceType, nil, nil, nil
case federationTypes.ComponentResourceType:
if len(path) != 0 {
return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path))
}
// Component resource, no path
return resourceType, nil, nil, nil
}
// return unhandled resource as-is
return resourceType, nil, nil, nil
}

View File

@@ -0,0 +1,121 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
Report struct {
*base
Res *types.Report
Sources []*ReportSource
Blocks []*ReportBlock
}
ReportSource struct {
*base
Res *types.ReportDataSource
}
ReportBlock struct {
*base
Res *types.ReportBlock
}
)
func NewReport(res *types.Report) *Report {
r := &Report{
base: &base{},
}
r.SetResourceType(types.ReportResourceType)
r.Res = res
r.AddIdentifier(identifiers(res.Handle, res.ID)...)
// Initial stamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
r.SetUserstamps(MakeUserstampsCUDO(res.CreatedBy, res.UpdatedBy, res.DeletedBy, res.OwnedBy))
return r
}
func (r *Report) Resource() interface{} {
return r.Res
}
func (r *Report) AddReportSource(res *types.ReportDataSource) *ReportSource {
s := &ReportSource{
base: &base{},
}
s.Res = res
r.Sources = append(r.Sources, s)
return s
}
func (r *Report) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.ReportRbacResourceTpl(), types.ReportResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// func (r *Report) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
// ref = r.Ref()
// path = nil
// resource = fmt.Sprintf(types.ReportResourceTranslationTpl(), types.ReportResourceTranslationType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
// return
// }
func (r *Report) AddReportBlock(res *types.ReportBlock) *ReportBlock {
p := &ReportBlock{
base: &base{},
}
p.Res = res
r.Blocks = append(r.Blocks, p)
return p
}
func (r *Report) SysID() uint64 {
return r.Res.ID
}
func (r *Report) encodeTranslations() ([]*ResourceTranslation, error) {
return nil, nil
}
// FindReport looks for the workflow in the resource set
func FindReport(rr InterfaceSet, ii Identifiers) (ns *types.Report) {
var wfRes *Report
rr.Walk(func(r Interface) error {
wr, ok := r.(*Report)
if !ok {
return nil
}
if wr.Identifiers().HasAny(ii) {
wfRes = wr
}
return nil
})
// Found it
if wfRes != nil {
return wfRes.Res
}
return nil
}
func ReportErrUnresolved(ii Identifiers) error {
return fmt.Errorf("report unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,232 @@
package resource
import (
"fmt"
"strconv"
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
base struct {
rt string
ii Identifiers
rr RefSet
ph bool
ts *Timestamps
us *Userstamps
cfg *EnvoyConfig
urefs RefSet
}
EnvoyConfig struct {
// SkipIf determines when the encoding should be skipped for this resource
SkipIf string
OnExisting MergeAlg
}
MergeAlg int
)
const (
// Default takes the operation defined default
Default MergeAlg = iota
// Skip skips the existing resource
Skip
// Replace replaces the existing resource
Replace
// MergeLeft updates the existing resource, giving priority to the existing data
MergeLeft
// MergeRight updates the existing resource, giving priority to the new data
MergeRight
)
// fn converts identifier values (string, fmt.Stringer, uint64) to string slice
//
// Each value is checked and should not be empty or zero
func identifiers(ii ...interface{}) []string {
ss := make([]string, 0, len(ii))
for _, i := range ii {
switch c := i.(type) {
case uint64:
if c == 0 {
continue
}
ss = append(ss, strconv.FormatUint(c, 10))
case fmt.Stringer:
if c.String() == "" {
continue
}
ss = append(ss, c.String())
case string:
if c == "" {
continue
}
ss = append(ss, c)
}
}
return ss
}
// Check checks if the identifier c is in the set of identifiers ii
func Check(a string, ii ...interface{}) bool {
for _, i := range ii {
switch pi := i.(type) {
case string:
if a == pi {
return true
}
case uint64:
if pi > 0 && strconv.FormatUint(pi, 10) == i {
return true
}
}
}
return false
}
// AddIdentifier adds a set of identifiers to the current resource
func (t *base) AddIdentifier(ss ...string) {
t.ii = t.ii.Add(ss...)
}
// SetIdentifier sets the identifiers to whatever was provided
func (t *base) SetIdentifier(ii Identifiers) {
t.ii = ii
}
// AddRef adds a new reference to the current resource
func (t *base) AddRef(rt string, ii ...string) *Ref {
iiC := make([]string, 0, len(ii))
for _, i := range ii {
if i != "" {
iiC = append(iiC, i)
}
}
return t.addRef(&Ref{ResourceType: rt, Identifiers: Identifiers{}.Add(iiC...)})
}
func (t *base) addRef(r *Ref) *Ref {
t.rr = append(t.rr, r)
return r
}
// ReplaceRef replaces the given ref with the new one.
// The new ref is added regardles if the old one exists or not.
func (t *base) ReplaceRef(old, new *Ref) {
t.rr = t.rr.replaceRef(old, new)
}
// SetResourceType sets the resource type of the current resource struct
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 := []*Userstamp{us.CreatedBy, us.UpdatedBy, us.DeletedBy, us.OwnedBy, us.RunAs}
t.SetUserRefs(uu)
}
}
func (t *base) Userstamps() *Userstamps {
return t.us
}
func (t *base) SetConfig(cfg *EnvoyConfig) {
t.cfg = cfg
}
func (t *base) Config() *EnvoyConfig {
return t.cfg
}
func (t *base) SetUserRefs(uu []*Userstamp) {
if t.urefs == nil {
t.urefs = make(RefSet, 0, 4)
}
for _, u := range uu {
if u == nil {
continue
}
if u.UserID > 0 {
t.urefs = append(t.urefs, t.AddRef(types.UserResourceType, strconv.FormatUint(u.UserID, 10)))
} else if u.Ref != "" {
t.urefs = append(t.urefs, t.AddRef(types.UserResourceType, u.Ref))
}
}
}
func (t *base) UserRefs() RefSet {
return t.urefs
}
func (t *base) Identifiers() Identifiers {
return t.ii
}
func (t *base) ResourceType() string {
return t.rt
}
func (t *base) Refs() RefSet {
return t.rr
}
func (t *base) Ref() *Ref {
return &Ref{ResourceType: t.rt, Identifiers: t.ii}
}
func (t *base) HasRefs() bool {
return t.rr == nil || len(t.rr) == 0
}
// MarkPlaceholder denotes that the given resource should be treated as a placeholder
//
// Placeholder resources should not be encoded but should only provide additional
// context to resources that depend on it
func (t *base) MarkPlaceholder() {
t.ph = true
}
// Placeholder resources should not be encoded but should only provide additional
// context to resources that depend on it
func (t *base) Placeholder() bool {
return t.ph
}
func IgnoreDepResolution(ref *Ref) bool {
return ref.ResourceType == composeTypes.ModuleFieldResourceType
}
func (t *base) ReID(ii Identifiers) {
t.SetIdentifier(ii)
}
func (t *base) ReRef(old RefSet, new RefSet) {
for i, o := range old {
t.ReplaceRef(o, new[i])
}
}

View File

@@ -0,0 +1,80 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
systemTypes "github.com/cortezaproject/corteza/server/system/types"
)
func (r *ComposeChart) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
tmp, err := r.encodeTranslations()
return append(out, tmp...), err
}
func (r *ComposeModule) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
tmp, err := r.encodeTranslations()
return append(out, tmp...), err
}
func (r *ComposeModuleField) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
return out, nil
}
func (r *ComposeNamespace) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
return out, nil
}
func (r *ComposePage) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
tmp, err := r.encodeTranslations()
return append(out, tmp...), err
}
func (r *ComposePageLayout) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
tmp, err := r.encodeTranslations()
return append(out, tmp...), err
}

View File

@@ -0,0 +1,92 @@
package resource
import (
"fmt"
"github.com/cortezaproject/corteza/server/system/types"
"golang.org/x/text/language"
)
type (
ResourceTranslation struct {
*base
Res types.ResourceTranslationSet
RefResource string
RefRes *Ref
RefPath []*Ref
Priority int
}
)
var (
defaultLanguage = language.English
)
func NewResourceTranslation(res types.ResourceTranslationSet, refResource string, refRes *Ref, refPath ...*Ref) *ResourceTranslation {
r := &ResourceTranslation{base: &base{}}
r.SetResourceType(ResourceTranslationType)
r.Res = res
r.RefResource = refResource
r.RefRes = r.addRef(refRes)
// any additional constraints
for i, rp := range refPath {
ref := MakeRef(rp.ResourceType, rp.Identifiers)
r.RefRes.Constraint(ref)
if i > 0 {
for j := i - 1; j < i; j++ {
aux := refPath[j]
ref = ref.Constraint(MakeRef(aux.ResourceType, aux.Identifiers))
}
}
r.RefPath = append(r.RefPath, r.addRef(ref))
}
return r
}
func (r *ResourceTranslation) Resource() interface{} {
return r.Res
}
func (r *ResourceTranslation) ReRef(old RefSet, new RefSet) {
r.base.ReRef(old, new)
for i, o := range old {
if o.equals(r.RefRes) {
r.RefRes = new[i]
break
}
}
for i, o := range old {
if RefSet(r.RefPath).findRef(o) > -1 {
r.RefPath = RefSet(r.RefPath).replaceRef(o, new[i])
}
}
}
func (l *ResourceTranslation) MarkDefault() {
l.Priority = 1
}
func (l *ResourceTranslation) IsDefault() bool {
return l.Priority == 1
}
func (l *ResourceTranslation) MarkGeneric() {
l.Priority = 0
}
func (l *ResourceTranslation) IsGeneric() bool {
return l.Priority == 0
}
func ResourceTranslationErrNotFound(ii Identifiers) error {
return fmt.Errorf("resource translation not found %v", ii.StringSlice())
}

View File

@@ -0,0 +1,115 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"fmt"
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
"strings"
)
// ParseResourceTranslation generates resource setting logic for each resource
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) {
if res == "" {
return "", nil, nil, fmt.Errorf("empty resource")
}
sp := "/"
if strings.Index(res, "corteza::") == 0 {
res = res[9:]
}
res = strings.TrimSpace(res)
res = strings.TrimRight(res, sp)
rr := strings.Split(res, sp)
// only service defined (corteza::system, corteza::compose, ...)
if len(rr) == 1 {
return "", nil, nil, fmt.Errorf("only service defined: %s", res)
}
// full thing
resourceType, path := rr[0], rr[1:]
for p := 1; p < len(path); p++ {
if path[p] == "*" {
return "", nil, nil, fmt.Errorf("path wildcard not allowed for locale resources: '%s'", res)
}
}
// make the resource provide the slice of parent resources we should nest under
switch resourceType {
case composeTypes.ChartResourceTranslationType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeChartResourceTranslationReferences(
path[0],
path[1],
)
return composeTypes.ChartResourceTranslationType, ref, pp, err
case composeTypes.ModuleResourceTranslationType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeModuleResourceTranslationReferences(
path[0],
path[1],
)
return composeTypes.ModuleResourceTranslationType, ref, pp, err
case composeTypes.ModuleFieldResourceTranslationType:
if len(path) != 3 {
return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeModuleFieldResourceTranslationReferences(
path[0],
path[1],
path[2],
)
return composeTypes.ModuleFieldResourceTranslationType, ref, pp, err
case composeTypes.NamespaceResourceTranslationType:
if len(path) != 1 {
return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeNamespaceResourceTranslationReferences(
path[0],
)
return composeTypes.NamespaceResourceTranslationType, ref, pp, err
case composeTypes.PageResourceTranslationType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposePageResourceTranslationReferences(
path[0],
path[1],
)
return composeTypes.PageResourceTranslationType, ref, pp, err
case composeTypes.PageLayoutResourceTranslationType:
if len(path) != 3 {
return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path))
}
ref, pp, err := ComposePageLayoutResourceTranslationReferences(
path[0],
path[1],
path[2],
)
return composeTypes.PageLayoutResourceTranslationType, ref, pp, err
}
// return unhandled resource as-is
return resourceType, nil, nil, nil
}

View File

@@ -0,0 +1,25 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Definitions file that controls how this file is generated:
// - automation.workflow.yaml
import (
"github.com/cortezaproject/corteza/server/automation/types"
)
// AutomationWorkflowResourceTranslationReferences generates Locale references
//
// Resources with "envoy: false" are skipped
//
// This function is auto-generated
func AutomationWorkflowResourceTranslationReferences(workflow string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.WorkflowResourceType, Identifiers: MakeIdentifiers(workflow)}
return
}

View File

@@ -0,0 +1,72 @@
package resource
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"github.com/cortezaproject/corteza/server/compose/types"
)
// ComposeChartResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposeChartResourceTranslationReferences(namespaceID string, self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.ChartResourceType, Identifiers: MakeIdentifiers(self)}
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
return
}
// ComposeModuleResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposeModuleResourceTranslationReferences(namespaceID string, self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(self)}
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
return
}
// ComposeModuleFieldResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposeModuleFieldResourceTranslationReferences(namespaceID string, moduleID string, self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.ModuleFieldResourceType, Identifiers: MakeIdentifiers(self)}
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(moduleID)})
return
}
// ComposeNamespaceResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposeNamespaceResourceTranslationReferences(self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(self)}
return
}
// ComposePageResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposePageResourceTranslationReferences(namespaceID string, self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(self)}
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
return
}
// ComposePageLayoutResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposePageLayoutResourceTranslationReferences(namespaceID string, pageID string, self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.PageLayoutResourceType, Identifiers: MakeIdentifiers(self)}
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
pp = append(pp, &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(pageID)})
return
}

View File

@@ -0,0 +1,73 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
// Role represents a Role
Role struct {
*base
Res *types.Role
}
)
func NewRole(rl *types.Role) *Role {
r := &Role{base: &base{}}
r.SetResourceType(types.RoleResourceType)
r.Res = rl
r.AddIdentifier(identifiers(rl.Handle, rl.Name, rl.ID)...)
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&rl.CreatedAt, rl.UpdatedAt, rl.DeletedAt, rl.ArchivedAt))
return r
}
func (r *Role) Resource() interface{} {
return r.Res
}
func (r *Role) SysID() uint64 {
return r.Res.ID
}
func (r *Role) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.RoleRbacResourceTpl(), types.RoleResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// FindRole looks for the role in the resources
func FindRole(rr InterfaceSet, ii Identifiers) (rl *types.Role) {
var rlRes *Role
rr.Walk(func(r Interface) error {
rr, ok := r.(*Role)
if !ok {
return nil
}
if rr.Identifiers().HasAny(ii) {
rlRes = rr
}
return nil
})
// Found it
if rlRes != nil {
return rlRes.Res
}
return nil
}
func RoleErrUnresolved(ii Identifiers) error {
return fmt.Errorf("role unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,57 @@
package resource
import (
"github.com/cortezaproject/corteza/server/system/types"
)
type (
Settings struct {
*base
Res types.SettingValueSet
}
Setting struct {
*base
Res types.SettingValue
}
)
func NewSettings(vv map[string]interface{}) *Settings {
r := &Settings{base: &base{}}
r.SetResourceType(SettingsResourceType)
r.Res = make(types.SettingValueSet, 0, len(vv))
for k, v := range vv {
sv := &types.SettingValue{
Name: k,
}
sv.SetSetting(v)
r.Res = append(r.Res, sv)
}
return r
}
func NewSetting(s *types.SettingValue) *Setting {
r := &Setting{base: &base{}}
r.SetResourceType(SettingsResourceType)
r.Res = *s
r.AddIdentifier(identifiers(s.Name)...)
// Set initial stamps
r.SetTimestamps(MakeTimestampsCUDA(nil, &s.UpdatedAt, nil, nil))
r.SetUserstamps(&Userstamps{
UpdatedBy: &Userstamp{UserID: s.UpdatedBy},
})
return r
}
func (r *Setting) Resource() interface{} {
return r.Res
}
func (r *Settings) Resource() interface{} {
return r.Res
}

View File

@@ -0,0 +1,64 @@
package resource
import "fmt"
type (
shaper interface {
Shape([]Interface) ([]Interface, error)
}
)
// Shape shapes ResourceDatasets based on their correlated Template
//
// During the shaping step, no data is removed, nor added to the original resource slice.
// The shapers are ran in the same order they were provided.
func Shape(rr []Interface, ss ...shaper) ([]Interface, error) {
ii := make([]Interface, 0, int(len(rr)/2))
// Firstly handle any resource shaping
for _, s := range ss {
sdd, err := s.Shape(rr)
if err != nil {
return nil, err
}
ii = append(ii, sdd...)
}
// Cleanup the final shaped resource slice.
// @todo make this cleaner!!1!'
//
// After this point, all of the data should be ready for further processing.
for _, r := range rr {
if _, ok := r.(*ComposeRecordTemplate); ok {
continue
}
if _, ok := r.(*ResourceDataset); ok {
continue
}
ii = append(ii, r)
}
return ii, nil
}
// findResourceDataset finds and returns the first dataset that matches the
// provided identifiers.
func findResourceDataset(rr []Interface, ii Identifiers) *ResourceDataset {
for _, r := range rr {
genR, ok := r.(*ResourceDataset)
if !ok {
continue
}
if !genR.Identifiers().HasAny(ii) {
continue
}
return genR
}
return nil
}
func genericSourceErrUnresolved(ii Identifiers) error {
return fmt.Errorf("generic source unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,73 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
// Template represents a Template
Template struct {
*base
Res *types.Template
}
)
func NewTemplate(t *types.Template) *Template {
r := &Template{base: &base{}}
r.SetResourceType(types.TemplateResourceType)
r.Res = t
r.AddIdentifier(identifiers(t.Handle, t.Meta.Short, t.ID)...)
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDAS(&t.CreatedAt, t.UpdatedAt, t.DeletedAt, nil, nil))
return r
}
func (r *Template) Resource() interface{} {
return r.Res
}
func (r *Template) SysID() uint64 {
return r.Res.ID
}
func (r *Template) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.TemplateRbacResourceTpl(), types.TemplateResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// FindTemplate looks for the template in the resources
func FindTemplate(rr InterfaceSet, ii Identifiers) (u *types.Template) {
var tRes *Template
rr.Walk(func(r Interface) error {
tr, ok := r.(*Template)
if !ok {
return nil
}
if tr.Identifiers().HasAny(ii) {
tRes = tr
}
return nil
})
// Found it
if tRes != nil {
return tRes.Res
}
return nil
}
func TemplateErrUnresolved(ii Identifiers) error {
return fmt.Errorf("template unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,119 @@
package resource
import (
"encoding/json"
"time"
)
type (
Timestamp struct {
// S is a stringified representation of the timestamp
S string
// T is a time representation of the timestamp
T *time.Time
// Tz is the timezone the timestamp will be encoded into
Tz string
// Tpl is the template the timestamp will be encoded into
Tpl string
}
Timestamps struct {
CreatedAt *Timestamp
UpdatedAt *Timestamp
DeletedAt *Timestamp
ArchivedAt *Timestamp
SuspendedAt *Timestamp
}
)
func (ts *Timestamp) MarshalYAML() (interface{}, error) {
return ts.S, nil
}
func (ts *Timestamp) MarshalJSON() ([]byte, error) {
return json.Marshal(ts.S)
}
// MakeTimestamp initializes a Timestamp from the passed timestamp
func MakeTimestamp(ts string) *Timestamp {
t := toTime(ts)
if t == nil {
return nil
}
return &Timestamp{
S: ts,
T: t,
}
}
// MakeTimestampsCUDA initializes timestamps for createdAt, updatedAt, deletedAt and archivedAt
func MakeTimestampsCUDA(c, u, d, a *time.Time) *Timestamps {
t := &Timestamps{}
if c != nil && !c.IsZero() {
t.CreatedAt = &Timestamp{T: c}
}
if u != nil && !u.IsZero() {
t.UpdatedAt = &Timestamp{T: u}
}
if d != nil && !d.IsZero() {
t.DeletedAt = &Timestamp{T: d}
}
if a != nil && !a.IsZero() {
t.ArchivedAt = &Timestamp{T: a}
}
return t
}
// MakeTimestampsCUDAS initializes timestamps for createdAt, updatedAt, deletedAt, archivedAt and suspendedAt
func MakeTimestampsCUDAS(c, u, d, a, s *time.Time) *Timestamps {
t := MakeTimestampsCUDA(c, u, d, a)
if s != nil && !s.IsZero() {
t.SuspendedAt = &Timestamp{T: s}
}
return t
}
// Model stringifies the timestamps based on the passed template and timezone
func (tt *Timestamps) Model(tpl string, tz string) (*Timestamps, error) {
var err error
if tt.CreatedAt != nil {
tt.CreatedAt, err = tt.CreatedAt.Model(tpl, tz)
}
if tt.UpdatedAt != nil {
tt.UpdatedAt, err = tt.UpdatedAt.Model(tpl, tz)
}
if tt.DeletedAt != nil {
tt.DeletedAt, err = tt.DeletedAt.Model(tpl, tz)
}
if tt.ArchivedAt != nil {
tt.ArchivedAt, err = tt.ArchivedAt.Model(tpl, tz)
}
if tt.SuspendedAt != nil {
tt.SuspendedAt, err = tt.SuspendedAt.Model(tpl, tz)
}
return tt, err
}
// Model stringifies the timestamp based on the passed template and timezone
func (ts *Timestamp) Model(tpl string, tz string) (*Timestamp, error) {
if tz != "" {
tzL, err := time.LoadLocation(tz)
if err != nil {
return nil, err
}
t := ts.T.In(tzL)
ts.T = &t
}
if tpl == "" {
tpl = time.RFC3339
}
ts.S = ts.T.Format(tpl)
return ts, nil
}

View File

@@ -0,0 +1,343 @@
package resource
import (
"strconv"
)
type (
Interface interface {
Identifiers() Identifiers
ResourceType() string
Resource() interface{}
Refs() RefSet
MarkPlaceholder()
Placeholder() bool
ReID(Identifiers)
ReRef(old RefSet, new RefSet)
}
ErrorWrapper interface {
WrapError(act string, err error) error
}
InterfaceSet []Interface
IdentifiableInterface interface {
Interface
SysID() uint64
}
RefableInterface interface {
Interface
Ref() string
}
RBACInterface interface {
Interface
RBACParts() (string, *Ref, []*Ref)
}
LocaleInterface interface {
Interface
ResourceTranslationParts() (string, *Ref, []*Ref)
EncodeTranslations() ([]*ResourceTranslation, error)
}
PrunableInterface interface {
Interface
Prune(*Ref)
}
RefSet []*Ref
Ref struct {
// @todo check with Denis regarding strings here (the cdocs comment)
// @todo should this become node type instead?
ResourceType string
Identifiers Identifiers
Constraints RefSet
}
Identifiers []string
)
var (
DataSourceResourceType = "data:raw"
SettingsResourceType = "setting"
RbacResourceType = "rbac-rule"
ResourceTranslationType = "resource-translation"
)
func MakeRef(rt string, ii Identifiers) *Ref {
return &Ref{ResourceType: rt, Identifiers: ii}
}
func MakeWildRef(rt string) *Ref {
return &Ref{ResourceType: rt, Identifiers: MakeIdentifiers("*")}
}
func MakeIdentifiers(ss ...string) Identifiers {
ii := make(Identifiers, 0, len(ss))
ii = ii.Add(ss...)
return ii
}
func (ri Identifiers) Add(ii ...string) Identifiers {
for _, i := range ii {
if len(i) > 0 {
ri = append(ri, i)
}
}
return ri
}
func (ri Identifiers) Clone() Identifiers {
out := make(Identifiers, 0, len(ri))
out = append(out, ri...)
return out
}
func (ri Identifiers) HasAny(check Identifiers) bool {
// The size of these will be tiny so no need for hashmaps
for _, i := range ri {
for _, j := range check {
if i == j {
return true
}
}
}
return false
}
func (ri Identifiers) StringSlice() []string {
return ri
}
func (ri Identifiers) First() string {
if len(ri) == 0 {
return ""
}
return ri[0]
}
func (ri Identifiers) FirstID() uint64 {
if len(ri) <= 0 {
return 0
}
for _, s := range ri {
if v, err := strconv.ParseUint(s, 10, 64); err != nil {
continue
} else {
return v
}
}
return 0
}
func (rr InterfaceSet) Walk(f func(r Interface) error) (err error) {
for _, r := range rr {
err = f(r)
if err != nil {
return
}
}
return nil
}
// SearchForIdentifiers returns the resources where the provided identifiers exist
//
// The Resource is matching if at least one identifier matches.
func (rr InterfaceSet) SearchForIdentifiers(resource string, ii Identifiers) (out InterfaceSet) {
out = make(InterfaceSet, 0, len(rr)/2)
for _, r := range rr {
if r.ResourceType() != resource {
continue
}
if r.Identifiers().HasAny(ii) {
out = append(out, r)
}
}
return
}
// SearchForReferences returns the resources where the provided references exist
//
// The Resource is matching if at least one reference matches.
func (rr InterfaceSet) SearchForReferences(ref *Ref) (out InterfaceSet) {
out = make(InterfaceSet, 0, len(rr)/2)
for _, r := range rr {
if r.Refs().HasRef(ref) {
out = append(out, r)
}
}
return
}
// Constraint returns the current reference with added constraint
func (r *Ref) Constraint(c *Ref) *Ref {
if r.Constraints == nil {
r.Constraints = make(RefSet, 0, 1)
}
r.Constraints = append(r.Constraints, &Ref{
ResourceType: c.ResourceType,
Identifiers: MakeIdentifiers(c.Identifiers.StringSlice()...),
})
return r
}
// IsWildcard checks if this Ref points to all resources of a specific resource type
func (r *Ref) IsWildcard() bool {
if len(r.Identifiers) == 0 {
return false
}
for _, i := range r.Identifiers {
if i == "*" {
return true
}
}
return false
}
func (a *Ref) equals(b *Ref) bool {
if a == nil && a == b {
return true
}
if a == nil && a != b {
return false
}
if a.ResourceType != b.ResourceType {
return false
}
if !b.IsWildcard() && !a.Identifiers.HasAny(b.Identifiers) {
return false
}
for _, c := range b.Constraints {
if !a.Constraints.HasRef(c) {
return false
}
}
return true
}
func (rr RefSet) findRef(ref *Ref) int {
for i, r := range rr {
if r.equals(ref) {
return i
}
}
return -1
}
// replaceRef replaces the reference both on the ref level and on the
// constraint level.
func (rr RefSet) replaceRef(old, new *Ref) RefSet {
found := false
for x := len(rr) - 1; x >= 0; x-- {
r := rr[x]
if r.equals(old) {
found = true
if new == nil {
rr[x] = rr[len(rr)-1]
return rr[:len(rr)-1]
} else {
rr[x] = new
}
} else {
if len(r.Constraints) > 0 {
r.Constraints = r.Constraints.replaceRef(old, new)
}
}
}
if !found && new != nil {
return append(rr, new)
}
return rr
}
func (rr RefSet) HasRef(ref *Ref) bool {
return rr.findRef(ref) > -1
}
// IsSbubset checks if aa is subset of bb
//
// Order is important; primary use is RBAC and res. tr. path checking.
func (aa RefSet) IsSubset(bb RefSet) bool {
if len(bb) > len(aa) {
return false
}
return aa[0:len(bb)].Equals(bb)
}
// Equals checks if two RefSets match
//
// They match if everything matches, even constraints (important)
func (aa RefSet) Equals(bb RefSet) bool {
if len(aa) != len(bb) {
return false
}
for i, a := range aa {
if !a.equals(bb[i]) {
return false
}
}
return true
}
// Unique returns only unique references
//
// Uniqueness is defined as "two references may not define
// the same resource type and identifier" combinations.
func (rr RefSet) Unique() RefSet {
out := make(RefSet, 0, len(rr))
seen := make(map[string]Identifiers)
for _, r := range rr {
ii, ok := seen[r.ResourceType]
// type not seen at all, unique
if !ok {
out = append(out, r)
seen[r.ResourceType] = r.Identifiers.Clone()
continue
}
// not yet seen
if !ii.HasAny(r.Identifiers) {
out = append(out, r)
seen[r.ResourceType] = seen[r.ResourceType].Add(r.Identifiers...)
}
}
return out
}

View File

@@ -0,0 +1,96 @@
package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
// User represents a User
User struct {
*base
Res *types.User
RoleMembership []Identifiers
RefRoles RefSet
}
)
func NewUser(u *types.User, roles ...string) *User {
r := &User{base: &base{}}
r.SetResourceType(types.UserResourceType)
r.Res = u
r.AddIdentifier(identifiers(u.Handle, u.Email, u.Name, u.ID)...)
// Role membership
for _, role := range roles {
rid := MakeIdentifiers(role)
r.RoleMembership = append(r.RoleMembership, rid)
r.RefRoles = append(r.RefRoles, r.AddRef(types.RoleResourceType, role))
}
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDAS(&u.CreatedAt, u.UpdatedAt, u.DeletedAt, nil, u.SuspendedAt))
return r
}
func (u *User) AddRoles(roles ...*types.Role) *User {
for _, r := range roles {
idf := firstOkString(r.Handle, strconv.FormatUint(r.ID, 10))
rid := MakeIdentifiers(idf)
u.RoleMembership = append(u.RoleMembership, rid)
u.RefRoles = append(u.RefRoles, u.AddRef(types.RoleResourceType, idf))
}
return u
}
func (r *User) Resource() interface{} {
return r.Res
}
func (r *User) SysID() uint64 {
return r.Res.ID
}
func (r *User) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.UserRbacResourceTpl(), types.UserResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle, r.Res.Username))
return
}
// FindUser looks for the user in the resources
func FindUser(rr InterfaceSet, ii Identifiers) (u *types.User) {
var uRes *User
rr.Walk(func(r Interface) error {
ur, ok := r.(*User)
if !ok {
return nil
}
if ur.Identifiers().HasAny(ii) {
uRes = ur
}
return nil
})
// Found it
if uRes != nil {
return uRes.Res
}
return nil
}
func UserErrUnresolved(ii Identifiers) error {
return fmt.Errorf("user unresolved %v", ii.StringSlice())
}

View File

@@ -0,0 +1,242 @@
package resource
import (
"encoding/json"
"fmt"
"strconv"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
Userstamp struct {
UserID uint64
Ref string
U *types.User
// S is the stringified user representation
S string
}
Userstamps struct {
CreatedBy *Userstamp
UpdatedBy *Userstamp
DeletedBy *Userstamp
OwnedBy *Userstamp
RunAs *Userstamp
}
UserstampIndex map[string]*Userstamp
)
func (us *Userstamp) MarshalYAML() (interface{}, error) {
if us == nil {
return nil, nil
}
if us.U != nil {
if us.U.Handle != "" {
return us.U.Handle, nil
}
if us.U.Username != "" {
return us.U.Username, nil
}
if us.U.Email != "" {
return us.U.Email, nil
}
if us.U.Name != "" {
return us.U.Name, nil
}
}
if us.Ref != "" {
return us.Ref, nil
}
if us.UserID > 0 {
return us.UserID, nil
}
return nil, fmt.Errorf("invalid userstamp")
}
func (us *Userstamp) MarshalJSON() ([]byte, error) {
if us == nil {
return nil, nil
}
l := ""
if us.U != nil {
if us.U.Handle != "" {
l = us.U.Handle
}
if us.U.Username != "" {
l = us.U.Username
}
if us.U.Email != "" {
l = us.U.Email
}
if us.U.Name != "" {
l = us.U.Name
}
} else {
if us.Ref != "" {
l = us.Ref
}
if us.UserID > 0 {
l = strconv.FormatUint(us.UserID, 10)
}
}
if l == "" {
return nil, fmt.Errorf("invalid userstamp")
}
return json.Marshal(l)
}
// MakeUserstamp initializes a userstamp from the passed user struct
func MakeUserstamp(u *types.User) *Userstamp {
sID := strconv.FormatUint(u.ID, 10)
return &Userstamp{
UserID: u.ID,
U: u,
Ref: firstOkString(u.Handle, u.Email, u.Username, sID),
}
}
// MakeUserstampFromRef initializes a userstamp from the passed reference
//
// If possible, the function determines the userID
func MakeUserstampFromRef(ref string) *Userstamp {
id, err := strconv.ParseUint(ref, 10, 64)
us := &Userstamp{}
if err == nil && id != 0 {
us.UserID = id
us.U = &types.User{ID: id}
}
us.Ref = ref
return us
}
// MakeUserstampFromRef initializes a userstamp from the passed userID
func MakeUserstampFromID(ID uint64) *Userstamp {
if ID == 0 {
return nil
}
return MakeUserstampFromRef(strconv.FormatUint(ID, 10))
}
// MakeUserstampsCUDO initializes userstamps for createdAt, updatedAt, deletedAt and ownedBy
func MakeUserstampsCUDO(c, u, d, o uint64) *Userstamps {
us := &Userstamps{}
if c > 0 {
us.CreatedBy = &Userstamp{
UserID: c,
Ref: strconv.FormatUint(c, 10),
}
}
if u > 0 {
us.UpdatedBy = &Userstamp{
UserID: u,
Ref: strconv.FormatUint(u, 10),
}
}
if d > 0 {
us.DeletedBy = &Userstamp{
UserID: d,
Ref: strconv.FormatUint(d, 10),
}
}
if o > 0 {
us.OwnedBy = &Userstamp{
UserID: o,
Ref: strconv.FormatUint(o, 10),
}
}
return us
}
// Model stringifies the userstamp
//
// @todo make this configurable
func (us *Userstamp) Model() (string, error) {
if us == nil {
return "", nil
}
if us.U != nil {
if us.U.Handle != "" {
return us.U.Handle, nil
}
if us.U.Username != "" {
return us.U.Username, nil
}
if us.U.Email != "" {
return us.U.Email, nil
}
if us.U.Name != "" {
return us.U.Name, nil
}
}
if us.Ref != "" {
return us.Ref, nil
}
if us.UserID > 0 {
return strconv.FormatUint(us.UserID, 10), nil
}
return "", fmt.Errorf("invalid userstamp")
}
func (ux UserstampIndex) Add(uu ...*types.User) {
for _, u := range uu {
sID := strconv.FormatUint(u.ID, 10)
s := MakeUserstamp(u)
ux[sID] = s
ux[u.Email] = s
if u.Handle != "" {
ux[u.Handle] = s
}
if u.Username != "" {
ux[u.Username] = s
}
if u.Name != "" {
ux[u.Name+" "+u.Email] = s
}
}
}
func (ux UserstampIndex) GetByKey(kr interface{}) *Userstamp {
if k, ok := kr.(string); ok {
return ux[k]
} else if k, ok := kr.(uint64); ok {
return ux[strconv.FormatUint(k, 10)]
}
return nil
}
func (ux UserstampIndex) GetByStamp(s *Userstamp) *Userstamp {
if s == nil {
return nil
}
if s.Ref != "" {
return ux.GetByKey(s.Ref)
}
if s.UserID > 0 {
return ux.GetByKey(s.UserID)
}
if s.U != nil && s.U.ID > 0 {
return ux.GetByKey(s.U.ID)
}
return s
}

View File

@@ -0,0 +1,80 @@
package resource
import (
"time"
"github.com/cortezaproject/corteza/server/compose/types"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
)
func firstOkString(ss ...string) string {
for _, s := range ss {
if s != "" && s != "0" {
return s
}
}
return ""
}
// Taken (and modified) 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
}
// Ref builders
func MakeNamespaceRef(id uint64, handle, name string) *Ref {
return makeGenericRef(types.NamespaceResourceType, id, handle, name)
}
func MakeModuleRef(id uint64, handle, name string) *Ref {
return makeGenericRef(types.ModuleResourceType, id, handle, name)
}
func MakePageRef(id uint64, handle, title string) *Ref {
return makeGenericRef(types.PageResourceType, id, handle, title)
}
func MakeRoleRef(id uint64, handle, name string) *Ref {
return makeGenericRef(systemTypes.RoleResourceType, id, handle, name)
}
func makeGenericRef(t string, id uint64, ii ...string) *Ref {
args := make([]interface{}, len(ii)+1)
args[0] = id
for i := range ii {
args[i+1] = ii[i]
}
aux := &Ref{
ResourceType: t,
Identifiers: identifiers(args...),
}
if t == "" || len(aux.Identifiers) == 0 {
return nil
}
return aux
}