CLI export tool for compose
This commit is contained in:
parent
6535034d87
commit
55da25788f
@ -2,14 +2,21 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
sqlTypes "github.com/jmoiron/sqlx/types"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/repository"
|
||||
"github.com/cortezaproject/corteza-server/compose/service"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/internal/auth"
|
||||
"github.com/cortezaproject/corteza-server/internal/permissions"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
)
|
||||
@ -18,102 +25,346 @@ func Exporter(ctx context.Context, c *cli.Config) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export",
|
||||
Long: `Specify one ("modules", "pages", "charts", "permissions") or more resources to export`,
|
||||
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
c.InitServices(ctx, c)
|
||||
|
||||
ctx = auth.SetSuperUserContext(ctx)
|
||||
|
||||
mm, _, err := service.DefaultModule.Find(types.ModuleFilter{NamespaceID: 88714882739863655})
|
||||
var (
|
||||
nsFlag = cmd.Flags().Lookup("namespace").Value.String()
|
||||
ns *types.Namespace
|
||||
err error
|
||||
)
|
||||
|
||||
if namespaceID, _ := strconv.ParseUint(nsFlag, 10, 64); namespaceID > 0 {
|
||||
ns, err = service.DefaultNamespace.FindByID(namespaceID)
|
||||
if err != repository.ErrNamespaceNotFound {
|
||||
cli.HandleError(err)
|
||||
}
|
||||
} else if ns, err = service.DefaultNamespace.FindByHandle(nsFlag); err != nil {
|
||||
if err != repository.ErrNamespaceNotFound {
|
||||
cli.HandleError(err)
|
||||
}
|
||||
}
|
||||
|
||||
modules, _, err := service.DefaultModule.Find(types.ModuleFilter{NamespaceID: ns.ID})
|
||||
cli.HandleError(err)
|
||||
|
||||
pages, _, err := service.DefaultPage.Find(types.PageFilter{NamespaceID: ns.ID})
|
||||
cli.HandleError(err)
|
||||
|
||||
charts, _, err := service.DefaultChart.Find(types.ChartFilter{NamespaceID: ns.ID})
|
||||
cli.HandleError(err)
|
||||
|
||||
y := yaml.NewEncoder(cmd.OutOrStdout())
|
||||
out := Compose{}
|
||||
|
||||
out := Compose{
|
||||
Modules: expModules(mm),
|
||||
for _, arg := range args {
|
||||
switch arg {
|
||||
case "module", "modules":
|
||||
out.Modules = expModules(modules)
|
||||
case "chart", "charts":
|
||||
out.Charts = expCharts(charts, modules)
|
||||
case "page", "pages":
|
||||
out.Pages = expPages(0, pages, modules, charts)
|
||||
case "allow", "deny", "permission", "permissions":
|
||||
out.Allow = expServicePermissions(permissions.Allow)
|
||||
out.Deny = expServicePermissions(permissions.Deny)
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = y, out
|
||||
cli.HandleError(y.Encode(out))
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().String("namespace", "crm", "Export namespace resources (by ID or string)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type (
|
||||
Compose struct {
|
||||
Modules []Module
|
||||
Modules map[string]Module `yaml:",omitempty"`
|
||||
Pages map[string]Page `yaml:",omitempty"`
|
||||
Charts map[string]Chart `yaml:",omitempty"`
|
||||
|
||||
Allow map[string]map[string][]string `yaml:",omitempty"`
|
||||
Deny map[string]map[string][]string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
Module struct {
|
||||
Name string
|
||||
Handle string
|
||||
Meta string
|
||||
Meta string `yaml:"meta,omitempty"`
|
||||
Fields map[string]Field
|
||||
|
||||
Allow map[string][]string `yaml:",omitempty"`
|
||||
Deny map[string][]string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
Field struct {
|
||||
Label string
|
||||
Label string `yaml:",omitempty"`
|
||||
Kind string
|
||||
|
||||
Options string
|
||||
Options types.ModuleFieldOptions `yaml:",omitempty"`
|
||||
|
||||
Private bool
|
||||
Required bool
|
||||
Visible bool
|
||||
Multi bool
|
||||
Private bool `yaml:",omitempty"`
|
||||
Required bool `yaml:",omitempty"`
|
||||
Visible bool `yaml:",omitempty"`
|
||||
Multi bool `yaml:",omitempty"`
|
||||
// DefaultValue string
|
||||
|
||||
Allow map[string][]string `yaml:",omitempty"`
|
||||
Deny map[string][]string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
Page struct {
|
||||
Module string `yaml:",omitempty"`
|
||||
Title string `yaml:",omitempty"`
|
||||
Description string `yaml:",omitempty"`
|
||||
|
||||
Blocks types.PageBlocks `yaml:",omitempty"`
|
||||
|
||||
Pages map[string]Page `yaml:",omitempty"`
|
||||
|
||||
Visible bool
|
||||
|
||||
Allow map[string][]string `yaml:",omitempty"`
|
||||
Deny map[string][]string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
Chart struct {
|
||||
Name string `yaml:",omitempty"`
|
||||
|
||||
Config types.ChartConfig `yaml:",omitempty"`
|
||||
|
||||
Allow map[string][]string `yaml:",omitempty"`
|
||||
Deny map[string][]string `yaml:",omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
func expModules(mm types.ModuleSet) (o []Module) {
|
||||
o = make([]Module, len(mm))
|
||||
func expModules(mm types.ModuleSet) (o map[string]Module) {
|
||||
o = map[string]Module{}
|
||||
|
||||
for i, m := range mm {
|
||||
o[i] = Module{
|
||||
for _, m := range mm {
|
||||
module := Module{
|
||||
Name: m.Name,
|
||||
Handle: makeHandle(m.Handle, m.Name),
|
||||
Meta: m.Meta.String(),
|
||||
Fields: expModuleFields(m.Fields),
|
||||
Fields: expModuleFields(m.Fields, mm),
|
||||
|
||||
Allow: expResourcePermissions(permissions.Allow, types.ModulePermissionResource),
|
||||
Deny: expResourcePermissions(permissions.Deny, types.ModulePermissionResource),
|
||||
}
|
||||
|
||||
if o[i].Handle == "" {
|
||||
h := regexp.MustCompile(`^[^A-Za-z][^0-9A-Za-z_\-.][^A-Za-z0-9]$`).ReplaceAllString(o[i].Name, "")
|
||||
if handle.IsValid(h) {
|
||||
o[i].Handle = h
|
||||
}
|
||||
if meta := expModuleMetaCleanup(m.Meta); len(meta) > 0 {
|
||||
module.Meta = meta
|
||||
}
|
||||
|
||||
handle := makeHandleFromName(m.Name, m.Handle, "module-id", m.ID)
|
||||
o[handle] = module
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func expModuleFields(ff types.ModuleFieldSet) (o map[string]Field) {
|
||||
func expModuleMetaCleanup(meta sqlTypes.JSONText) string {
|
||||
var aux interface{}
|
||||
err := meta.Unmarshal(&aux)
|
||||
cli.HandleError(err)
|
||||
|
||||
if kv, ok := aux.(map[string]interface{}); !ok {
|
||||
return ""
|
||||
} else if _, has := kv["admin"]; has {
|
||||
delete(kv, "admin")
|
||||
if len(kv) == 0 {
|
||||
return ""
|
||||
}
|
||||
meta, err = json.Marshal(kv)
|
||||
cli.HandleError(err)
|
||||
}
|
||||
|
||||
return meta.String()
|
||||
}
|
||||
|
||||
func expModuleFields(ff types.ModuleFieldSet, modules types.ModuleSet) (o map[string]Field) {
|
||||
o = make(map[string]Field)
|
||||
|
||||
for _, f := range ff {
|
||||
o[f.Name] = Field{
|
||||
Label: f.Label,
|
||||
Kind: f.Kind,
|
||||
Options: f.Options.String(),
|
||||
Options: expModuleFieldOptions(f.Options, modules),
|
||||
Private: f.Private,
|
||||
Required: f.Required,
|
||||
Visible: f.Visible,
|
||||
Multi: f.Multi,
|
||||
|
||||
Allow: expResourcePermissions(permissions.Allow, types.ModuleFieldPermissionResource),
|
||||
Deny: expResourcePermissions(permissions.Deny, types.ModuleFieldPermissionResource),
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func makeHandle(h, n string) string {
|
||||
if h == "" {
|
||||
h = regexp.MustCompile(`^[^A-Za-z][^0-9A-Za-z_\-.][^A-Za-z0-9]$`).ReplaceAllString(n, "")
|
||||
if handle.IsValid(h) {
|
||||
return h
|
||||
func expModuleFieldOptions(opt types.ModuleFieldOptions, modules types.ModuleSet) types.ModuleFieldOptions {
|
||||
out := opt
|
||||
|
||||
if moduleIDstr, has := out["moduleID"].(string); has {
|
||||
delete(out, "moduleID")
|
||||
out["module"] = "Error: module with ID " + moduleIDstr + " does not exist"
|
||||
if moduleID, _ := strconv.ParseUint(moduleIDstr, 10, 64); moduleID > 0 {
|
||||
if module := modules.FindByID(moduleID); module != nil {
|
||||
out["module"] = makeHandleFromName(module.Name, module.Handle, "module-%d", module.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n
|
||||
return out
|
||||
}
|
||||
|
||||
func expPages(parentID uint64, pages types.PageSet, modules types.ModuleSet, charts types.ChartSet) (o map[string]Page) {
|
||||
var (
|
||||
children = pages.FindByParent(parentID)
|
||||
handle string
|
||||
)
|
||||
o = map[string]Page{}
|
||||
|
||||
for _, child := range children {
|
||||
page := Page{
|
||||
Title: child.Title,
|
||||
Description: child.Description,
|
||||
Blocks: expPageBlocks(child.Blocks, pages, modules, charts),
|
||||
Pages: expPages(child.ID, pages, modules, charts),
|
||||
Visible: child.Visible,
|
||||
|
||||
Allow: expResourcePermissions(permissions.Allow, types.PagePermissionResource),
|
||||
Deny: expResourcePermissions(permissions.Deny, types.PagePermissionResource),
|
||||
}
|
||||
|
||||
if child.ModuleID > 0 {
|
||||
m := modules.FindByID(child.ModuleID)
|
||||
if m == nil {
|
||||
page.Module = fmt.Sprintf("Error: module with ID %d does not exist", child.ModuleID)
|
||||
} else {
|
||||
page.Module = makeHandleFromName(m.Name, m.Handle, "module-%d", child.ModuleID)
|
||||
|
||||
if child.Handle == "" {
|
||||
// Reuse module's handle for page
|
||||
handle = makeHandleFromName(m.Name, m.Handle, "record-page-%d", child.ModuleID)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handle = makeHandleFromName(child.Title, child.Handle, "page-%d", child.ID)
|
||||
}
|
||||
|
||||
o[handle] = page
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func expPageBlocks(in types.PageBlocks, pages types.PageSet, modules types.ModuleSet, charts types.ChartSet) types.PageBlocks {
|
||||
out := types.PageBlocks(in)
|
||||
|
||||
for i := range out {
|
||||
if ff, has := out[i].Options["fields"].([]interface{}); has {
|
||||
// Trim out obsolete field info
|
||||
for fi := range ff {
|
||||
if f, ok := ff[fi].(map[string]interface{}); ok {
|
||||
ff[fi] = map[string]string{
|
||||
"name": f["name"].(string),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if moduleIDstr, has := out[i].Options["moduleID"].(string); has {
|
||||
delete(out[i].Options, "moduleID")
|
||||
out[i].Options["module"] = "Error: module with ID " + moduleIDstr + " does not exist"
|
||||
if moduleID, _ := strconv.ParseUint(moduleIDstr, 10, 64); moduleID > 0 {
|
||||
if module := modules.FindByID(moduleID); module != nil {
|
||||
out[i].Options["module"] = makeHandleFromName(module.Name, module.Handle, "module-%d", module.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pageIDstr, has := out[i].Options["pageID"].(string); has {
|
||||
delete(out[i].Options, "pageID")
|
||||
out[i].Options["page"] = "Error: page with ID " + pageIDstr + " does not exist"
|
||||
if pageID, _ := strconv.ParseUint(pageIDstr, 10, 64); pageID > 0 {
|
||||
if page := pages.FindByID(pageID); page != nil {
|
||||
out[i].Options["page"] = makeHandleFromName(page.Title, page.Handle, "page-%d", page.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if chartIDstr, has := out[i].Options["chartID"].(string); has {
|
||||
delete(out[i].Options, "chartID")
|
||||
out[i].Options["chart"] = "Error: chart with ID " + chartIDstr + " does not exist"
|
||||
if chartID, _ := strconv.ParseUint(chartIDstr, 10, 64); chartID > 0 {
|
||||
if chart := charts.FindByID(chartID); chart != nil {
|
||||
out[i].Options["chart"] = makeHandleFromName(chart.Name, chart.Handle, "chart-%d", chart.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func expCharts(charts types.ChartSet, modules types.ModuleSet) (o map[string]Chart) {
|
||||
o = map[string]Chart{}
|
||||
|
||||
for _, c := range charts {
|
||||
chart := Chart{
|
||||
Name: c.Name,
|
||||
Config: c.Config,
|
||||
|
||||
Allow: expResourcePermissions(permissions.Allow, types.ChartPermissionResource),
|
||||
Deny: expResourcePermissions(permissions.Deny, types.ChartPermissionResource),
|
||||
}
|
||||
// @todo moduleID => module Handle (CONFIG)
|
||||
|
||||
handle := makeHandleFromName(c.Name, c.Handle, "chart-%d", c.ID)
|
||||
|
||||
o[handle] = chart
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func expServicePermissions(access permissions.Access) map[string]map[string][]string {
|
||||
// @todo fetch all known roles
|
||||
// @todo iterate over roles
|
||||
// @todo iterate over service.DefaultPermissions.FindRulesByRoleID()
|
||||
// @todo filter out all matching types.ComposePermissionResource
|
||||
// @todo fill return value
|
||||
return nil
|
||||
}
|
||||
|
||||
func expResourcePermissions(access permissions.Access, resource permissions.Resource) map[string][]string {
|
||||
// @todo fetch all known roles
|
||||
// @todo iterate over roles
|
||||
// @todo iterate over service.DefaultPermissions.FindRulesByRoleID()
|
||||
// @todo filter out all matching resource param
|
||||
// @todo fill return value
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeHandleFromName(name, currentHandle, def string, id uint64) string {
|
||||
if currentHandle != "" {
|
||||
return currentHandle
|
||||
}
|
||||
|
||||
newHandle := strings.ReplaceAll(name, " ", "_")
|
||||
newHandle = regexp.MustCompile(`[^0-9A-Za-z_\-.]+`).ReplaceAllString(newHandle, "")
|
||||
if handle.IsValid(newHandle) {
|
||||
return newHandle
|
||||
}
|
||||
|
||||
return fmt.Sprintf(def, id)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user