Remove handle requirement from resource API
We removed handle requirement for all resource like namespace(slug), module, user, page, chart. Due to that namespace import/export was broken as we were using handle as reference, hence fixed it by replacing handle usage with ID of resource. It also fixes index of template table for handle column.
This commit is contained in:
committed by
Katrin Yordanova
parent
16082d1766
commit
1699e3f94b
@@ -154,7 +154,6 @@ endpoints:
|
||||
title: Duplicate name
|
||||
- type: string
|
||||
name: slug
|
||||
required: true
|
||||
title: Duplicate slug
|
||||
- name: export
|
||||
path: "/{namespaceID}/export/{filename}.zip"
|
||||
@@ -201,7 +200,6 @@ endpoints:
|
||||
title: Imported namespace name
|
||||
- type: string
|
||||
name: slug
|
||||
required: true
|
||||
title: Imported namespace slug
|
||||
- name: triggerScript
|
||||
method: POST
|
||||
|
||||
@@ -39,10 +39,11 @@ type (
|
||||
}
|
||||
|
||||
namespaceImportSession struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"handle"`
|
||||
SessionID uint64 `json:"sessionID,string"`
|
||||
UserID uint64 `json:"userID,string"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"handle"`
|
||||
NamespaceID uint64 `json:"namespaceID,string"`
|
||||
SessionID uint64 `json:"sessionID,string"`
|
||||
UserID uint64 `json:"userID,string"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
@@ -285,12 +286,14 @@ func (svc namespace) Clone(ctx context.Context, namespaceID uint64, dup *types.N
|
||||
aProps.setNamespace(targetNs)
|
||||
|
||||
// - destination namespace
|
||||
dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
if dstNs != nil {
|
||||
return NamespaceErrHandleNotUnique()
|
||||
if dup.Slug != "" {
|
||||
dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
if dstNs != nil {
|
||||
return NamespaceErrHandleNotUnique()
|
||||
}
|
||||
}
|
||||
|
||||
// Access control
|
||||
@@ -403,6 +406,8 @@ func (svc namespace) ImportInit(ctx context.Context, f multipart.File, size int6
|
||||
return NamespaceErrImportMissingNamespace()
|
||||
}
|
||||
|
||||
// session needs to have namespaceID if ns Handle is not provided
|
||||
session.NamespaceID = ns.ID
|
||||
session.Name = ns.Name
|
||||
session.Slug = ns.Slug
|
||||
namespaceSessionStore[session.SessionID] = session
|
||||
@@ -425,17 +430,19 @@ func (svc namespace) ImportRun(ctx context.Context, sessionID uint64, dup *types
|
||||
return err
|
||||
}
|
||||
|
||||
if dup.Slug == "" || !handle.IsValid(dup.Slug) {
|
||||
if !handle.IsValid(dup.Slug) {
|
||||
return NamespaceErrInvalidHandle()
|
||||
}
|
||||
|
||||
// check for duplicate
|
||||
dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
if dstNs != nil {
|
||||
return NamespaceErrHandleNotUnique()
|
||||
if dup.Slug != "" {
|
||||
// check for duplicate
|
||||
dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug)
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
if dstNs != nil {
|
||||
return NamespaceErrHandleNotUnique()
|
||||
}
|
||||
}
|
||||
|
||||
// session
|
||||
@@ -453,7 +460,7 @@ func (svc namespace) ImportRun(ctx context.Context, sessionID uint64, dup *types
|
||||
|
||||
aProps.setNamespace(dup)
|
||||
|
||||
newNS, err = svc.envoyRun(ctx, session.Resources, &types.Namespace{Slug: session.Slug, Name: session.Name}, dup, encoder)
|
||||
newNS, err = svc.envoyRun(ctx, session.Resources, &types.Namespace{ID: session.NamespaceID, Slug: session.Slug, Name: session.Name}, dup, encoder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ func (n *composeChart) Encode(ctx context.Context, doc *Document, state *envoy.R
|
||||
|
||||
func (c *composeChart) MarshalYAML() (interface{}, error) {
|
||||
nn, err := makeMap(
|
||||
"chartID", c.res.ID,
|
||||
"handle", c.res.Handle,
|
||||
"name", c.res.Name,
|
||||
"config", c.chartConfig,
|
||||
|
||||
@@ -58,6 +58,9 @@ func (wrap *composeChart) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch k.Value {
|
||||
case "chartID":
|
||||
return y7s.DecodeScalar(v, "chart ID", &wrap.res.ID)
|
||||
|
||||
case "handle":
|
||||
return y7s.DecodeScalar(v, "chart handle", &wrap.res.Handle)
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ func (c *composeModule) MarshalYAML() (interface{}, error) {
|
||||
}
|
||||
|
||||
nn, err := makeMap(
|
||||
"moduleID", c.res.ID,
|
||||
"handle", c.res.Handle,
|
||||
"name", c.res.Name,
|
||||
"meta", auxMeta,
|
||||
|
||||
@@ -63,6 +63,9 @@ func (wrap *composeModule) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch k.Value {
|
||||
case "moduleID":
|
||||
return y7s.DecodeScalar(v, "module ID", &wrap.res.ID)
|
||||
|
||||
case "name":
|
||||
return y7s.DecodeScalar(v, "module name", &wrap.res.Name)
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ func (c *composeNamespace) MarshalYAML() (interface{}, error) {
|
||||
var err error
|
||||
|
||||
nn, err := makeMap(
|
||||
"namespaceID", c.res.ID,
|
||||
"name", c.res.Name,
|
||||
"slug", c.res.Slug,
|
||||
"enabled", c.res.Enabled,
|
||||
|
||||
@@ -75,6 +75,9 @@ func (wrap *composeNamespace) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
|
||||
return y7s.Each(n, func(k, v *yaml.Node) (err error) {
|
||||
switch k.Value {
|
||||
case "namespaceID":
|
||||
return y7s.DecodeScalar(v, "namespace ID", &wrap.res.ID)
|
||||
|
||||
case "modules":
|
||||
return v.Decode(&wrap.modules)
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ func (wrap *composePage) UnmarshalYAML(n *yaml.Node) (err error) {
|
||||
|
||||
return y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
||||
switch strings.ToLower(k.Value) {
|
||||
case "id", "pageid":
|
||||
case "id", "pageid", "pageID":
|
||||
return y7s.DecodeScalar(v, "page ID", &wrap.res.ID)
|
||||
|
||||
case "title":
|
||||
|
||||
@@ -3,9 +3,9 @@ package yaml
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/handle"
|
||||
"github.com/cortezaproject/corteza/server/pkg/y7s"
|
||||
. "github.com/cortezaproject/corteza/server/pkg/y7s"
|
||||
|
||||
@@ -23,6 +23,10 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
validReference = regexp.MustCompile(`^[A-Za-z1-9][0-9A-Za-z_\-.]*[A-Za-z0-9]$`)
|
||||
)
|
||||
|
||||
func Decoder() *decoder {
|
||||
return &decoder{}
|
||||
}
|
||||
@@ -68,10 +72,14 @@ func decodeRef(n *yaml.Node, refType string, ref *string) error {
|
||||
return y7s.NodeErr(n, "%s reference must be scalar", refType)
|
||||
}
|
||||
|
||||
if !handle.IsValid(n.Value) {
|
||||
return y7s.NodeErr(n, "%s reference must be a valid handle", refType)
|
||||
if !IsValidRef(n.Value) {
|
||||
return y7s.NodeErr(n, "%s reference must be a valid ID or Handle", refType)
|
||||
}
|
||||
|
||||
*ref = n.Value
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsValidRef(s string) bool {
|
||||
return len(s) >= 2 && validReference.MatchString(s)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza/server/pkg/envoy/resource"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
@@ -92,7 +92,11 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p1 == nil {
|
||||
return "", resource.ComposeNamespaceErrUnresolved(res.RefRes.Identifiers)
|
||||
}
|
||||
p1ID = p1.Slug
|
||||
if p1.Slug == "" {
|
||||
p1ID = cast.ToString(p1.ID)
|
||||
} else {
|
||||
p1ID = p1.Slug
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(composeTypes.NamespaceResourceTranslationTpl(), composeTypes.NamespaceResourceTranslationType, p1ID), nil
|
||||
@@ -103,7 +107,12 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p0 == nil {
|
||||
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
|
||||
}
|
||||
p0ID = p0.Slug
|
||||
|
||||
if p0.Slug == "" {
|
||||
p0ID = cast.ToString(p0.ID)
|
||||
} else {
|
||||
p0ID = p0.Slug
|
||||
}
|
||||
}
|
||||
|
||||
if res.RefRes != nil {
|
||||
@@ -111,7 +120,12 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p1 == nil {
|
||||
return "", resource.ComposeModuleErrUnresolved(res.RefRes.Identifiers)
|
||||
}
|
||||
p1ID = p1.Handle
|
||||
|
||||
if p1.Handle == "" {
|
||||
p1ID = cast.ToString(p1.ID)
|
||||
} else {
|
||||
p1ID = p1.Handle
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(composeTypes.ModuleResourceTranslationTpl(), composeTypes.ModuleResourceTranslationType, p0ID, p1ID), nil
|
||||
@@ -122,7 +136,12 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p0 == nil {
|
||||
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
|
||||
}
|
||||
p0ID = p0.Slug
|
||||
|
||||
if p0.Slug == "" {
|
||||
p0ID = cast.ToString(p0.ID)
|
||||
} else {
|
||||
p0ID = p0.Slug
|
||||
}
|
||||
}
|
||||
|
||||
if res.RefRes != nil {
|
||||
@@ -132,7 +151,7 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
}
|
||||
|
||||
if p1.Handle == "" {
|
||||
p1ID = strconv.FormatUint(p1.ID, 10)
|
||||
p1ID = cast.ToString(p1.ID)
|
||||
} else {
|
||||
p1ID = p1.Handle
|
||||
}
|
||||
@@ -145,13 +164,23 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p0 == nil {
|
||||
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
|
||||
}
|
||||
p0ID = p0.Slug
|
||||
|
||||
if p0.Slug == "" {
|
||||
p0ID = cast.ToString(p0.ID)
|
||||
} else {
|
||||
p0ID = p0.Slug
|
||||
}
|
||||
|
||||
p1 := resource.FindComposeModule(state.ParentResources, res.RefPath[1].Identifiers)
|
||||
if p1 == nil {
|
||||
return "", resource.ComposeModuleErrUnresolved(res.RefPath[1].Identifiers)
|
||||
}
|
||||
p1ID = p1.Handle
|
||||
|
||||
if p1.Handle == "" {
|
||||
p1ID = cast.ToString(p1.ID)
|
||||
} else {
|
||||
p1ID = p1.Handle
|
||||
}
|
||||
|
||||
// field
|
||||
f := resource.FindComposeModuleField(state.ParentResources, res.RefPath[1].Identifiers, res.RefRes.Identifiers)
|
||||
@@ -168,7 +197,12 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p0 == nil {
|
||||
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
|
||||
}
|
||||
p0ID = p0.Slug
|
||||
|
||||
if p0.Slug == "" {
|
||||
p0ID = cast.ToString(p0.ID)
|
||||
} else {
|
||||
p0ID = p0.Slug
|
||||
}
|
||||
}
|
||||
|
||||
if res.RefRes != nil {
|
||||
@@ -176,7 +210,12 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
|
||||
if p1 == nil {
|
||||
return "", resource.ComposeChartErrUnresolved(res.RefRes.Identifiers)
|
||||
}
|
||||
p1ID = p1.Handle
|
||||
|
||||
if p1.Handle == "" {
|
||||
p1ID = cast.ToString(p1.ID)
|
||||
} else {
|
||||
p1ID = p1.Handle
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(composeTypes.ChartResourceTranslationTpl(), composeTypes.ChartResourceTranslationType, p0ID, p1ID), nil
|
||||
|
||||
Generated
+4
-4
@@ -2328,10 +2328,10 @@ var Template = &dal.Model{
|
||||
},
|
||||
|
||||
&dal.Index{
|
||||
Ident: "templates_uniqueLanguageHandle",
|
||||
Type: "BTREE",
|
||||
Unique: true,
|
||||
|
||||
Ident: "templates_uniqueLanguageHandle",
|
||||
Type: "BTREE",
|
||||
Unique: true,
|
||||
Predicate: "handle != '' AND deleted_at IS NULL",
|
||||
Fields: []*dal.IndexField{
|
||||
{
|
||||
AttributeIdent: "Language",
|
||||
|
||||
@@ -871,7 +871,7 @@ endpoints:
|
||||
path: "/"
|
||||
parameters:
|
||||
post:
|
||||
- { name: handle, type: "string", required: true }
|
||||
- { name: handle, type: "string", }
|
||||
- { name: type, type: "string", required: true }
|
||||
- { name: meta, type: "types.ConnectionMeta", required: true, parser: "types.ParseConnectionMeta" }
|
||||
- { name: config, type: "types.ConnectionConfig", required: true, parser: "types.ParseConnectionConfig" }
|
||||
@@ -883,7 +883,7 @@ endpoints:
|
||||
parameters:
|
||||
path: [ { type: uint64, name: connectionID, required: true, title: "Connection ID" } ]
|
||||
post:
|
||||
- { name: handle, type: "string", required: true }
|
||||
- { name: handle, type: "string", }
|
||||
- { name: type, type: "string", required: true }
|
||||
- { name: meta, type: "types.ConnectionMeta", required: true, parser: "types.ParseConnectionMeta" }
|
||||
- { name: config, type: "types.ConnectionConfig", required: true, parser: "types.ParseConnectionConfig" }
|
||||
|
||||
@@ -49,11 +49,11 @@ template: {
|
||||
indexes: {
|
||||
"primary": { attribute: "id" }
|
||||
"unique_language_handle": {
|
||||
unique: true
|
||||
fields: [
|
||||
{ attribute: "language" },
|
||||
{ attribute: "handle", modifier: [ "LOWERCASE" ] }
|
||||
]
|
||||
predicate: "handle != '' AND deleted_at IS NULL"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user