3
0
corteza/compose/importer/namespace.go
2019-10-01 14:23:33 +02:00

294 lines
7.5 KiB
Go

package importer
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/deinterfacer"
"github.com/cortezaproject/corteza-server/pkg/importer"
)
type (
Namespace struct {
imp *Importer
set types.NamespaceSet
dirty map[uint64]bool
// modules per namespace
modules map[string]*Module
// charts per namespace
charts map[string]*Chart
// pages per namespace
pages map[string]*Page
// records per namespace
records map[string]*Record
// a.scripts per namespace
scripts map[string]*AutomationScript
}
// @todo remove finder strategy, directly provide set of items
namespaceFinder interface {
Find(filter types.NamespaceFilter) (set types.NamespaceSet, f types.NamespaceFilter, err error)
}
namespaceKeeper interface {
Update(*types.Namespace) (*types.Namespace, error)
Create(*types.Namespace) (*types.Namespace, error)
}
)
func NewNamespaceImporter(imp *Importer) *Namespace {
out := &Namespace{
imp: imp,
set: types.NamespaceSet{},
dirty: make(map[uint64]bool),
modules: map[string]*Module{},
charts: map[string]*Chart{},
pages: map[string]*Page{},
records: map[string]*Record{},
scripts: map[string]*AutomationScript{},
}
if imp.namespaceFinder != nil {
out.set, _, _ = imp.namespaceFinder.Find(types.NamespaceFilter{})
}
return out
}
// CastSet resolves permission rules:
// { <namespace-handle>: { namespace } } or [ { namespace }, ... ]
func (nsImp *Namespace) CastSet(set interface{}) error {
return deinterfacer.Each(set, func(index int, handle string, def interface{}) error {
if index > -1 {
// Namespaces defined as collection
deinterfacer.KVsetString(&handle, "slug", def)
deinterfacer.KVsetString(&handle, "handle", def, handle)
}
return nsImp.Cast(handle, def)
})
}
// Cast resolves permission rules:
// { <namespace-handle>: { namespace } } or [ { namespace }, ... ]
func (nsImp *Namespace) Cast(handle string, def interface{}) (err error) {
if !deinterfacer.IsMap(def) {
return errors.New("expecting map of values for namespace")
}
var namespace *types.Namespace
if !importer.IsValidHandle(handle) {
return errors.New("invalid namespace handle")
}
handle = importer.NormalizeHandle(handle)
if namespace, err = nsImp.Get(handle); err != nil {
return err
} else if namespace == nil {
namespace = &types.Namespace{
Slug: handle,
Name: handle,
Enabled: true,
}
} else if namespace.ID == 0 {
// We will ignore that namespace has already been defined because
// we want to support multiple calls to Cast() fn, ie when namespace
// config is split into multiple files
} else {
nsImp.dirty[namespace.ID] = true
}
nsImp.Setup(namespace)
return deinterfacer.Each(def, func(_ int, key string, val interface{}) (err error) {
switch key {
case "handle", "slug":
// already handled
case "name", "title", "label":
namespace.Name = deinterfacer.ToString(val)
case "enabled":
namespace.Enabled = deinterfacer.ToBool(val)
case "meta":
namespace.Meta, err = nsImp.castMeta(namespace, val)
return
case "modules":
return nsImp.castModules(handle, val)
case "charts":
return nsImp.castCharts(handle, val)
case "pages":
return nsImp.castPages(handle, val)
case "records":
return nsImp.castRecords(handle, val)
case "scripts":
return nsImp.castScripts(handle, val)
case "allow", "deny":
return nsImp.imp.permissions.CastSet(types.NamespacePermissionResource.String()+namespace.Slug, key, val)
default:
return fmt.Errorf("unexpected key %q for namespace %q", key, namespace.Slug)
}
return err
})
}
func (nsImp *Namespace) castMeta(ns *types.Namespace, def interface{}) (types.NamespaceMeta, error) {
var meta = types.NamespaceMeta{}
return meta, deinterfacer.Each(def, func(_ int, key string, val interface{}) (err error) {
switch key {
case "subtitle":
meta.Subtitle = deinterfacer.ToString(val)
case "description":
meta.Description = deinterfacer.ToString(val)
default:
return fmt.Errorf("unexpected key %q for namespace %q meta", key, ns.Slug)
}
return
})
}
func (nsImp *Namespace) castModules(handle string, def interface{}) error {
if nsImp.modules[handle] == nil {
return fmt.Errorf("unknown namespace %q", handle)
}
return nsImp.modules[handle].CastSet(def)
}
func (nsImp *Namespace) castCharts(handle string, def interface{}) error {
if nsImp.charts[handle] == nil {
return fmt.Errorf("unknown namespace %q", handle)
}
return nsImp.charts[handle].CastSet(def)
}
func (nsImp *Namespace) castPages(handle string, def interface{}) error {
if nsImp.pages[handle] == nil {
return fmt.Errorf("unknown namespace %q", handle)
}
return nsImp.pages[handle].CastSet(def)
}
func (nsImp *Namespace) castRecords(handle string, def interface{}) error {
if nsImp.records[handle] == nil {
return fmt.Errorf("unknown namespace %q", handle)
}
return nsImp.records[handle].CastSet(def)
}
func (nsImp *Namespace) castScripts(handle string, def interface{}) error {
if nsImp.scripts[handle] == nil {
return fmt.Errorf("unknown namespace %q", handle)
}
return nsImp.scripts[handle].CastSet(def)
}
// Get finds or creates a new namespace
func (nsImp *Namespace) Get(handle string) (*types.Namespace, error) {
handle = importer.NormalizeHandle(handle)
if !importer.IsValidHandle(handle) {
return nil, errors.New("invalid namespace handle")
}
return nsImp.set.FindByHandle(handle), nil
}
func (nsImp *Namespace) Setup(namespace *types.Namespace) {
if nsImp.set.FindByHandle(namespace.Slug) == nil {
nsImp.set = append(nsImp.set, namespace)
}
if _, has := nsImp.modules[namespace.Slug]; !has {
nsImp.modules[namespace.Slug] = NewModuleImporter(nsImp.imp, namespace)
nsImp.pages[namespace.Slug] = NewPageImporter(nsImp.imp, namespace)
nsImp.charts[namespace.Slug] = NewChartImporter(nsImp.imp, namespace)
nsImp.scripts[namespace.Slug] = NewAutomationImporter(nsImp.imp, namespace)
nsImp.records[namespace.Slug] = NewRecordImporter(nsImp.imp, namespace)
}
}
func (nsImp *Namespace) Store(ctx context.Context, nsk namespaceKeeper, mk moduleKeeper, ck chartKeeper, pk pageKeeper, rk recordKeeper, sk automationScriptKeeper) error {
return nsImp.set.Walk(func(namespace *types.Namespace) (err error) {
var handle = namespace.Slug
if namespace.ID == 0 {
namespace, err = nsk.Create(namespace)
} else if nsImp.dirty[namespace.ID] {
namespace, err = nsk.Update(namespace)
}
if err != nil {
return
}
nsImp.dirty[namespace.ID] = false
nsImp.imp.permissions.UpdateResources(types.NamespacePermissionResource.String(), handle, namespace.ID)
if _, ok := nsImp.modules[handle]; ok {
nsImp.modules[handle].namespace = namespace
if err = nsImp.modules[handle].Store(ctx, mk); err != nil {
return errors.Wrap(err, "could not import modules")
}
nsImp.scripts[handle].namespace = namespace
if err = nsImp.scripts[handle].Store(ctx, sk); err != nil {
return errors.Wrap(err, "could not import automation scripts")
}
nsImp.charts[handle].namespace = namespace
if err = nsImp.charts[handle].Store(ctx, ck); err != nil {
return errors.Wrap(err, "could not import charts")
}
nsImp.pages[handle].namespace = namespace
if err = nsImp.pages[handle].Store(ctx, pk); err != nil {
return errors.Wrap(err, "could not import pages")
}
nsImp.records[handle].namespace = namespace
if err = nsImp.records[handle].Store(ctx, rk); err != nil {
return errors.Wrap(err, "could not import records")
}
}
return
})
}