feat(app,manifest): validation + extendable metadatas
All checks were successful
arcad/edge/pipeline/head This commit looks good
All checks were successful
arcad/edge/pipeline/head This commit looks good
This commit is contained in:
28
pkg/app/metadata/minimum_role.go
Normal file
28
pkg/app/metadata/minimum_role.go
Normal file
@ -0,0 +1,28 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"forge.cadoles.com/arcad/edge/pkg/app"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func WithMinimumRoleValidator(roles ...string) app.MetadataValidator {
|
||||
return func(metadata map[string]any) (bool, error) {
|
||||
rawMinimumRole, exists := metadata["minimumRole"]
|
||||
if !exists {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
minimumRole, ok := rawMinimumRole.(string)
|
||||
if !ok {
|
||||
return false, errors.Errorf("metadata['minimumRole']: unexpected value type '%T'", rawMinimumRole)
|
||||
}
|
||||
|
||||
for _, r := range roles {
|
||||
if minimumRole == r {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, errors.Errorf("metadata['minimumRole']: unexpected role '%s'", minimumRole)
|
||||
}
|
||||
}
|
56
pkg/app/metadata/named_path.go
Normal file
56
pkg/app/metadata/named_path.go
Normal file
@ -0,0 +1,56 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"forge.cadoles.com/arcad/edge/pkg/app"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type NamedPath string
|
||||
|
||||
const (
|
||||
NamedPathAdmin NamedPath = "admin"
|
||||
NamedPathIcon NamedPath = "icon"
|
||||
)
|
||||
|
||||
func WithNamedPathsValidator(names ...NamedPath) app.MetadataValidator {
|
||||
set := map[NamedPath]struct{}{}
|
||||
for _, n := range names {
|
||||
set[n] = struct{}{}
|
||||
}
|
||||
|
||||
return func(metadata map[string]any) (bool, error) {
|
||||
rawPaths, exists := metadata["paths"]
|
||||
if !exists {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
paths, ok := rawPaths.(map[any]any)
|
||||
if !ok {
|
||||
return false, errors.Errorf("metadata['paths']: unexpected named path value type '%T'", rawPaths)
|
||||
}
|
||||
|
||||
for n, p := range paths {
|
||||
name, ok := n.(string)
|
||||
if !ok {
|
||||
return false, errors.Errorf("metadata['paths']: unexpected named path type '%T'", n)
|
||||
}
|
||||
|
||||
if _, exists := set[NamedPath(name)]; !exists {
|
||||
return false, errors.Errorf("metadata['paths']: unexpected named path '%s'", name)
|
||||
}
|
||||
|
||||
path, ok := p.(string)
|
||||
if !ok {
|
||||
return false, errors.Errorf("metadata['paths']['%s']: unexpected named path value type '%T'", name, path)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return false, errors.Errorf("metadata['paths']['%s']: named path value should start with a '/'", name)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
}
|
7
pkg/app/metadata/testdata/manifests/invalid-minimum-role.yml
vendored
Normal file
7
pkg/app/metadata/testdata/manifests/invalid-minimum-role.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
id: foo.arcad.app
|
||||
version: v0.0.0
|
||||
title: Foo
|
||||
description: A test app
|
||||
tags: ["test"]
|
||||
metadata:
|
||||
minimumRole: foo
|
10
pkg/app/metadata/testdata/manifests/invalid-paths.yml
vendored
Normal file
10
pkg/app/metadata/testdata/manifests/invalid-paths.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
id: foo.arcad.app
|
||||
version: v0.0.0
|
||||
title: Foo
|
||||
description: A test app
|
||||
tags: ["test"]
|
||||
metadata:
|
||||
paths:
|
||||
invalid: /admin
|
||||
icon: /my-app-icon.png
|
||||
minimumRole: visitor
|
10
pkg/app/metadata/testdata/manifests/valid.yml
vendored
Normal file
10
pkg/app/metadata/testdata/manifests/valid.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
id: foo.arcad.app
|
||||
version: v0.0.0
|
||||
title: Foo
|
||||
description: A test app
|
||||
tags: ["test"]
|
||||
metadata:
|
||||
paths:
|
||||
admin: /admin
|
||||
icon: /my-app-icon.png
|
||||
minimumRole: visitor
|
74
pkg/app/metadata/validator_test.go
Normal file
74
pkg/app/metadata/validator_test.go
Normal file
@ -0,0 +1,74 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"forge.cadoles.com/arcad/edge/pkg/app"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type validatorTestCase struct {
|
||||
File string
|
||||
ExpectValid bool
|
||||
ExpectError bool
|
||||
}
|
||||
|
||||
var validatorTestCases = []validatorTestCase{
|
||||
{
|
||||
File: "valid.yml",
|
||||
ExpectValid: true,
|
||||
},
|
||||
{
|
||||
File: "invalid-paths.yml",
|
||||
ExpectValid: false,
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
File: "invalid-minimum-role.yml",
|
||||
ExpectValid: false,
|
||||
ExpectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
var validators = []app.MetadataValidator{
|
||||
WithMinimumRoleValidator("visitor", "user", "superuser", "admin", "superadmin"),
|
||||
WithNamedPathsValidator(NamedPathAdmin, NamedPathIcon),
|
||||
}
|
||||
|
||||
func TestManifestValidate(t *testing.T) {
|
||||
for _, tc := range validatorTestCases {
|
||||
func(tc *validatorTestCase) {
|
||||
t.Run(tc.File, func(t *testing.T) {
|
||||
data, err := ioutil.ReadFile(filepath.Join("testdata/manifests", tc.File))
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", errors.WithStack(err))
|
||||
}
|
||||
|
||||
var manifest app.Manifest
|
||||
|
||||
if err := yaml.Unmarshal(data, &manifest); err != nil {
|
||||
t.Fatalf("%+v", errors.WithStack(err))
|
||||
}
|
||||
|
||||
valid, err := manifest.Validate(validators...)
|
||||
|
||||
t.Logf("[RESULT] valid:%v, err:%v", valid, err)
|
||||
|
||||
if e, g := tc.ExpectValid, valid; e != g {
|
||||
t.Errorf("valid: expected '%v', got '%v'", e, g)
|
||||
}
|
||||
|
||||
if tc.ExpectError && err == nil {
|
||||
t.Error("err should not be nil")
|
||||
}
|
||||
|
||||
if !tc.ExpectError && err != nil {
|
||||
t.Errorf("err: expected nil, got '%+v'", err)
|
||||
}
|
||||
})
|
||||
}(&tc)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user