Moving server files to ./server
This commit is contained in:
19
server/codegen/schema/codegen.cue
Normal file
19
server/codegen/schema/codegen.cue
Normal file
@@ -0,0 +1,19 @@
|
||||
package schema
|
||||
|
||||
#_ioSpec: {
|
||||
template: string
|
||||
output: string
|
||||
|
||||
syntax: string | *"go"
|
||||
if output =~ "\\.adoc$" {
|
||||
syntax: "adoc"
|
||||
}
|
||||
}
|
||||
|
||||
#codegen: {
|
||||
#_ioSpec
|
||||
payload: _
|
||||
} | {
|
||||
bulk?: [...#_ioSpec]
|
||||
payload: _
|
||||
}
|
||||
28
server/codegen/schema/component.cue
Normal file
28
server/codegen/schema/component.cue
Normal file
@@ -0,0 +1,28 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
#component: #_base & {
|
||||
// copy field values from #_base
|
||||
handle: handle, ident: ident, expIdent: expIdent
|
||||
|
||||
label: strings.ToTitle(ident)
|
||||
platform: #baseHandle
|
||||
|
||||
resources: {
|
||||
[key=_]: {"handle": key, "component": handle, "platform": platform} & #Resource
|
||||
}
|
||||
|
||||
fqrt: platform + "::" + handle
|
||||
|
||||
// All known RBAC operations for this component
|
||||
rbac: #rbacComponent & {
|
||||
operations: {
|
||||
grant: {
|
||||
description: "Manage \(handle) permissions"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
server/codegen/schema/locale.cue
Normal file
54
server/codegen/schema/locale.cue
Normal file
@@ -0,0 +1,54 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"list"
|
||||
)
|
||||
|
||||
#locale: {
|
||||
resourceExpIdent: #expIdent
|
||||
|
||||
// @todo we need a better name here!
|
||||
skipSvc: bool | *false
|
||||
|
||||
extended: bool | *false
|
||||
|
||||
resource: {
|
||||
type: string
|
||||
const: string | *("\(resourceExpIdent)ResourceTranslationType")
|
||||
}
|
||||
|
||||
keys: {
|
||||
[key=_]: #localeKey & {
|
||||
name: key
|
||||
_resourceExpIdent: resourceExpIdent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#localeKey: {
|
||||
name: #handle
|
||||
_resourceExpIdent: #expIdent
|
||||
|
||||
path: [...(#ident | {part: #ident, var: bool | *false})] | *([name])
|
||||
|
||||
expandedPath: [ for p in path {
|
||||
if (p & {"p": #ident}) != _|_ {p, var: p.var}
|
||||
if (p & string) != _|_ {"part": p, var: false}
|
||||
}]
|
||||
|
||||
_suffix: strings.Join([ for p in expandedPath {strings.ToTitle(p.part)}], "")
|
||||
|
||||
struct: string | *("LocaleKey" + _resourceExpIdent + _suffix)
|
||||
|
||||
// As soon as we use vars in the path,
|
||||
// custom handler must be present
|
||||
_hasVars: list.Contains([ for p in path {p.var | false}], true)
|
||||
customHandler: bool | *_hasVars
|
||||
|
||||
if customHandler {
|
||||
decodeFunc: string | *("decodeTranslations" + _suffix)
|
||||
encodeFunc: string | *("encodeTranslations" + _suffix)
|
||||
serviceFunc: string | *("handle" + _resourceExpIdent + _suffix)
|
||||
}
|
||||
}
|
||||
265
server/codegen/schema/model.cue
Normal file
265
server/codegen/schema/model.cue
Normal file
@@ -0,0 +1,265 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
#Model: {
|
||||
ident: string
|
||||
attributes: {
|
||||
[name=_]: { "name": name }
|
||||
} & {
|
||||
[string]: #ModelAttribute
|
||||
}
|
||||
|
||||
indexes: ({
|
||||
[name=_]: { "name": name, "modelIdent": ident } & #ModelIndex
|
||||
} & {
|
||||
[string]: #ModelIndex
|
||||
}) | *({})
|
||||
}
|
||||
|
||||
|
||||
// logic in struct fields is a bit different
|
||||
#ModelAttribute: {
|
||||
name: #ident
|
||||
_words: strings.Replace(strings.Replace(name, "_", " ", -1), ".", " ", -1)
|
||||
|
||||
_ident: strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1))
|
||||
|
||||
// Golang type (built-in or other)
|
||||
goType: string | *"string"
|
||||
|
||||
// lowercase (unexported, golang) identifier
|
||||
ident: #ident | *_ident
|
||||
|
||||
// uppercase (exported, golang) identifier
|
||||
expIdent: #expIdent | *strings.ToTitle(ident)
|
||||
|
||||
// store identifier
|
||||
// @todo this should be moved to dal.ident
|
||||
storeIdent: #ident | *name
|
||||
|
||||
// enable or disable store for this attribute
|
||||
// @todo we should use dal prop for this, and extend it to support bool "false"
|
||||
// so that it can be disabled
|
||||
store: bool | *true
|
||||
|
||||
unique: bool | *false
|
||||
sortable: bool | *false
|
||||
descending: bool | *false
|
||||
ignoreCase: bool | *false
|
||||
|
||||
// currently disabled since not used by anything
|
||||
// it adds more than 4x overhead to the time it takes to generate the store code!
|
||||
// #ModelAttributeJsonTag
|
||||
|
||||
dal?: #ModelAttributeDal
|
||||
}
|
||||
|
||||
#ModelAttributeDal: {
|
||||
type: #ModelAttributeDalType | *"Text"
|
||||
|
||||
fqType: "dal.Type\(type)"
|
||||
|
||||
nullable: bool | *false
|
||||
|
||||
|
||||
if type == "ID" {
|
||||
generatedByStore: bool | *false
|
||||
default?: 0
|
||||
}
|
||||
|
||||
if type == "Ref" {
|
||||
refModelResType: #FQRT
|
||||
attribute: #handle | *"id"
|
||||
default?: 0
|
||||
}
|
||||
|
||||
if type == "Timestamp" {
|
||||
timezone: bool | *false
|
||||
precision: number | *(-1)
|
||||
defaultCurrentTimestamp?: true
|
||||
}
|
||||
|
||||
if type == "Time" {
|
||||
timezone: bool | *false
|
||||
precision: number | *(-1)
|
||||
defaultCurrentTimestamp?: true
|
||||
}
|
||||
|
||||
if type == "Date" {
|
||||
defaultCurrentTimestamp?: true
|
||||
}
|
||||
|
||||
if type == "Number" {
|
||||
precision: number | *(-1)
|
||||
scale: number | *(-1)
|
||||
default?: number
|
||||
meta?: { [string]: _ }
|
||||
}
|
||||
|
||||
if type == "Text" {
|
||||
length: number | *0
|
||||
default?: string
|
||||
}
|
||||
|
||||
if type == "Boolean" {
|
||||
default?: bool
|
||||
}
|
||||
|
||||
if type == "Enum" {
|
||||
values: []
|
||||
default?: string
|
||||
}
|
||||
|
||||
if type == "Geometry" {}
|
||||
|
||||
if type == "JSON" {
|
||||
default?: string | bytes
|
||||
defaultEmptyObject?: true
|
||||
}
|
||||
|
||||
if type == "Blob" {
|
||||
default?: bytes
|
||||
}
|
||||
|
||||
if type == "UUID" {}
|
||||
}
|
||||
|
||||
#ModelAttributeDalType:
|
||||
"ID" | "Ref" |
|
||||
"Timestamp" | "Time" | "Date" |
|
||||
"Number" |
|
||||
"Text" |
|
||||
"Boolean" |
|
||||
"Enum" |
|
||||
"Geometry" |
|
||||
"JSON" |
|
||||
"Blob" |
|
||||
"UUID"
|
||||
|
||||
|
||||
IdField: {
|
||||
// Expecting ID field to always have name ID
|
||||
name: "id"
|
||||
expIdent: "ID"
|
||||
unique: true
|
||||
|
||||
// @todo someday we'll replace this with the "ID" type
|
||||
goType: "uint64"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
HandleField: {
|
||||
// Expecting ID field to always have name handle
|
||||
name: "handle"
|
||||
unique: true
|
||||
ignoreCase: true
|
||||
|
||||
goType: "string"
|
||||
dal: { type: "Text", length: 64 }
|
||||
}
|
||||
|
||||
AttributeUserRef: {
|
||||
goType: "uint64"
|
||||
dal: { type: "Ref", refModelResType: "corteza::system:user", default: 0 }
|
||||
}
|
||||
|
||||
SortableTimestampField: {
|
||||
sortable: true
|
||||
goType: "time.Time"
|
||||
dal: { type: "Timestamp", timezone: true, nullable: false }
|
||||
}
|
||||
|
||||
SortableTimestampNowField: {
|
||||
sortable: true
|
||||
goType: "time.Time"
|
||||
dal: { type: "Timestamp", timezone: true, nullable: false, defaultCurrentTimestamp: true }
|
||||
}
|
||||
|
||||
SortableTimestampNilField: {
|
||||
sortable: true
|
||||
goType: "*time.Time"
|
||||
dal: { type: "Timestamp", timezone: true, nullable: true }
|
||||
}
|
||||
|
||||
#ModelAttributeJsonTag: {
|
||||
name: string
|
||||
|
||||
_specs: {field: string | *name, omitEmpty: bool | *false, "string": bool | *false}
|
||||
|
||||
json: string | _specs | bool | *false
|
||||
jsonTag?: string
|
||||
|
||||
// just wrap whatever we got in json
|
||||
if (json & string) != _|_ {
|
||||
jsonTag: "json:\"\(json)\""
|
||||
}
|
||||
|
||||
// json enable,d wrap with ident as a JSON prop name
|
||||
if (json & bool) != _|_ && json {
|
||||
// generic json tag
|
||||
jsonTag: "json:\"\(name)\""
|
||||
}
|
||||
|
||||
// full-specs
|
||||
if (json & bool) == _|_ && (json & _specs) != _|_ {
|
||||
_omitEmpty: string | *""
|
||||
if json.omitEmpty {
|
||||
_omitEmpty: ",omitempty"
|
||||
}
|
||||
_string: string | *""
|
||||
if json.string {
|
||||
_string: ",string"
|
||||
}
|
||||
|
||||
jsonTag: "json:\"\(json.field)\(_omitEmpty)\(_string)\""
|
||||
}
|
||||
}
|
||||
|
||||
#ModelIndex: close({
|
||||
name: #ident
|
||||
modelIdent: #ident
|
||||
_attributes: { [_]: #ModelAttribute }
|
||||
_words: strings.Replace(strings.Replace(name, "_", " ", -1), ".", " ", -1)
|
||||
|
||||
_ident: strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1))
|
||||
|
||||
// lowercase (unexported, golang) identifier
|
||||
ident: #ident | *"\(modelIdent)_\(_ident)"
|
||||
|
||||
primary: bool | *(strings.ToLower(name) == "primary")
|
||||
unique: bool | *(strings.Contains(name, "unique") || primary)
|
||||
|
||||
type: "BTREE" | *"BTREE"
|
||||
|
||||
// index predicate,
|
||||
// condition that must be met for the index to be used
|
||||
predicate?: string
|
||||
|
||||
attribute?: string
|
||||
attributes?: [string, ...]
|
||||
fields: [#ModelIndexField, ...]
|
||||
|
||||
if attribute != _|_ {
|
||||
attributes: [string, ...] & [attribute]
|
||||
}
|
||||
|
||||
if fields != _|_ && attributes != _|_ {
|
||||
fields: [
|
||||
for a in attributes {
|
||||
{"attribute": a} & #ModelIndexField
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
#IndexFieldModifier: "LOWERCASE"
|
||||
|
||||
#ModelIndexField: close({
|
||||
attribute: string
|
||||
modifiers?: [#IndexFieldModifier, ...]
|
||||
length?: number
|
||||
sort?: "DESC" | "ASC"
|
||||
nulls?: "FIRST" | "LAST"
|
||||
})
|
||||
49
server/codegen/schema/options.cue
Normal file
49
server/codegen/schema/options.cue
Normal file
@@ -0,0 +1,49 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
#_ENV: =~"^[A-Z][A-Z0-9_]*[A-Z0-9]?$"
|
||||
//#_optName: =~ "^[a-zA-Z][a-zA-Z0-9\\s]*[a-zA-Z0-9]+$"
|
||||
|
||||
#optionsGroup: #_base & {
|
||||
imports: [...string] | *([])
|
||||
|
||||
handle: #handle
|
||||
|
||||
title: string | *handle
|
||||
description?: string
|
||||
|
||||
env: #_ENV | *(strings.ToUpper(strings.Replace(handle, "-", "_", -1)))
|
||||
_envPrefix: env
|
||||
|
||||
options: {
|
||||
[_opt=_]: #option & {
|
||||
handle: _opt
|
||||
env: #_ENV | *(_envPrefix + "_" + strings.ToUpper(strings.Replace(handle, " ", "_", -1)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#option: {
|
||||
handle: #handle
|
||||
_words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1)
|
||||
|
||||
// lowercased (unexported, golang) identifier
|
||||
ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1))
|
||||
|
||||
// upercased (exported, golang) identifier
|
||||
expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1)
|
||||
|
||||
type: string | *"string"
|
||||
description?: string
|
||||
|
||||
// Default expression to be used
|
||||
defaultGoExpr?: string
|
||||
|
||||
env?: #_ENV
|
||||
|
||||
// Plain default value to use when generating .env.example
|
||||
defaultValue?: string
|
||||
}
|
||||
21
server/codegen/schema/platform.cue
Normal file
21
server/codegen/schema/platform.cue
Normal file
@@ -0,0 +1,21 @@
|
||||
package schema
|
||||
|
||||
#platform: {
|
||||
ident: #baseHandle | *"corteza"
|
||||
|
||||
options: [...#optionsGroup]
|
||||
|
||||
components: [...{platform: ident} & #component]
|
||||
|
||||
resources: {
|
||||
[key=#handle]: #Resource & {
|
||||
"handle": key,
|
||||
"platform": ident
|
||||
}
|
||||
}
|
||||
|
||||
// automation: {
|
||||
// types: ....
|
||||
// function ....
|
||||
// }
|
||||
}
|
||||
61
server/codegen/schema/rbac.cue
Normal file
61
server/codegen/schema/rbac.cue
Normal file
@@ -0,0 +1,61 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
#rbacComponent: {
|
||||
resource: {
|
||||
type: string
|
||||
}
|
||||
|
||||
operations: {
|
||||
[key=_]: #rbacOperation & {
|
||||
handle: key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#rbacResource: {
|
||||
resourceExpIdent: #expIdent
|
||||
|
||||
operations: {
|
||||
[key=_]: #rbacOperation & {
|
||||
handle: key
|
||||
_resourceExpIdent: resourceExpIdent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#rbacOperation: {
|
||||
handle: #handle
|
||||
description: string | *handle
|
||||
_resourceExpIdent?: string
|
||||
|
||||
// Some string manipulation that will result in
|
||||
// more pronouncable access-control check function name
|
||||
|
||||
// When check function name is not explicitly defined we try
|
||||
// to use resource and operation name and generate easy-to-read name
|
||||
//
|
||||
// <res> + <op> => Can<Op><Res>
|
||||
// <res> + <op:foo.bar.verb> => Can<Verb><Foo><Bar>On<Res>
|
||||
|
||||
_operation: strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1)
|
||||
_opSplit: strings.Split(_operation, ".")
|
||||
|
||||
_opFlip: [_opSplit[len(_opSplit)-1]] + _opSplit[0:len(_opSplit)-1]
|
||||
_opFinal: strings.Replace(strings.ToTitle(strings.Join(_opFlip, " ")), " ", "", -1)
|
||||
|
||||
if _resourceExpIdent == _|_ {
|
||||
checkFuncName: #expIdent | *"Can\(_opFinal)"
|
||||
}
|
||||
|
||||
if len(_opSplit) > 1 && _resourceExpIdent != _|_ {
|
||||
checkFuncName: #expIdent | *"Can\(_opFinal)On\(_resourceExpIdent)"
|
||||
}
|
||||
|
||||
if len(_opSplit) <= 1 && _resourceExpIdent != _|_ {
|
||||
checkFuncName: #expIdent | *"Can\(_opFinal)\(_resourceExpIdent)"
|
||||
}
|
||||
}
|
||||
137
server/codegen/schema/resource.cue
Normal file
137
server/codegen/schema/resource.cue
Normal file
@@ -0,0 +1,137 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// fully qualified resource type
|
||||
#FQRT: =~ "^corteza::(compose|system|federation|automation):[a-z][a-z0-9-]*$"
|
||||
|
||||
#Resource: {
|
||||
#_base
|
||||
|
||||
// type: #resourceType | *""
|
||||
|
||||
imports: [...{ import: string }]
|
||||
|
||||
// copy field values from #_base
|
||||
handle: handle, ident: ident, expIdent: expIdent
|
||||
|
||||
component: #baseHandle | *"component"
|
||||
platform: #baseHandle | *"corteza"
|
||||
|
||||
// Fully qualified resource name
|
||||
fqrt: #FQRT | *(platform + "::" + component + ":" + handle)
|
||||
|
||||
model: #Model & {
|
||||
// use resource handle (plural) as model ident as default
|
||||
// model ident represents a db table or a container name
|
||||
ident: string | *"\(strings.Replace(handle, "-", "_", -1))s"
|
||||
}
|
||||
|
||||
filter: {
|
||||
"expIdent": #expIdent | *"\(expIdent)Filter"
|
||||
|
||||
struct: {
|
||||
[name=_]: {"name": name} & #ModelAttribute
|
||||
}
|
||||
|
||||
// generate filtering by-nil-state for the specified fields
|
||||
"byNilState": [...string]
|
||||
|
||||
// generate filtering by-false-state for the specified fields
|
||||
"byFalseState": [...string]
|
||||
|
||||
// generate query filter for the specified fields
|
||||
"query": [...string]
|
||||
|
||||
// filter resources by fields (eq)
|
||||
"byValue": [...string]
|
||||
}
|
||||
// operations: #Operations
|
||||
|
||||
features: {
|
||||
// filtering by label
|
||||
labels: bool | *true
|
||||
|
||||
// filtering by flag
|
||||
flags: bool | *false
|
||||
|
||||
// support pagination
|
||||
paging: bool | *true
|
||||
|
||||
// support sorting
|
||||
sorting: bool | *true
|
||||
|
||||
// support resource check function
|
||||
checkFn: bool | *true
|
||||
}
|
||||
|
||||
// All parent resources
|
||||
parents: [... #_base & {
|
||||
// copy field values from #_base
|
||||
handle: handle, ident: ident, expIdent: expIdent
|
||||
|
||||
refField: #expIdent | *(expIdent + "ID")
|
||||
param: #ident | *(ident + "ID")
|
||||
}]
|
||||
|
||||
// All known RBAC operations for this resource
|
||||
rbac?: #rbacResource & {
|
||||
resourceExpIdent: expIdent
|
||||
}
|
||||
|
||||
locale?: #locale & {
|
||||
resourceExpIdent: expIdent
|
||||
resource: {
|
||||
// @todo can we merge this with RBAC type (FQRN?)
|
||||
type: component + ":" + handle
|
||||
}
|
||||
}
|
||||
|
||||
store?: {
|
||||
// how is this resource represented (prefixed/suffixed functions) in the store
|
||||
"ident": #ident | *ident
|
||||
"identPlural": #ident | *"\(store.ident)s"
|
||||
"expIdent": #expIdent | *strings.ToTitle(store.ident)
|
||||
"expIdentPlural": #expIdent | *"\(store.expIdent)s"
|
||||
|
||||
api?: {
|
||||
lookups: [...{
|
||||
_expFields: [ for f in fields {strings.ToTitle(model.attributes[f].expIdent)}]
|
||||
|
||||
"expIdent": "Lookup\(store.expIdent)By" + strings.Join(_expFields, "")
|
||||
description: string | *""
|
||||
|
||||
// fields used for the lookup (must exist in the struct)
|
||||
fields: [...string]
|
||||
|
||||
// Skip null constraints
|
||||
nullConstraint: [...string]
|
||||
constraintCheck: bool | *false
|
||||
}]
|
||||
|
||||
functions: [...{
|
||||
expIdent: string
|
||||
|
||||
description: string | *""
|
||||
|
||||
args: [...{ident: #ident, goType: string, spread: bool | *false}]
|
||||
return: [...string]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#storeFunction: {
|
||||
expIdent: #expIdent
|
||||
args: [...string]
|
||||
return: [...string]
|
||||
}
|
||||
|
||||
#PkgResource: #Resource & {
|
||||
package: {
|
||||
ident: #ident
|
||||
import: string
|
||||
}
|
||||
}
|
||||
39
server/codegen/schema/shared.cue
Normal file
39
server/codegen/schema/shared.cue
Normal file
@@ -0,0 +1,39 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
// Identifier
|
||||
#ident: =~"^[a-z][a-zA-Z0-9_]*$"
|
||||
|
||||
// Exported identifier
|
||||
#expIdent: =~"^[A-Z][a-zA-Z0-9_]*$"
|
||||
|
||||
// More liberal then identifier, allows underscores and dots
|
||||
#handle: =~"^[A-Za-z][a-zA-Z0-9_\\-\\.]*[a-zA-Z0-9]+$"
|
||||
|
||||
// More liberal then identifier
|
||||
#baseHandle: =~"^[a-z][a-z0-9-]*[a-z0-9]+$"
|
||||
|
||||
#_base: {
|
||||
// lowercase dash-separated words
|
||||
// used to build ident and exported identifiers
|
||||
handle: #baseHandle
|
||||
_words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1)
|
||||
|
||||
// lowercase (unexported, golang) identifier
|
||||
ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1))
|
||||
|
||||
// plural
|
||||
identPlural: #ident | *"\(ident)s"
|
||||
|
||||
// uppercase (exported, golang) identifier
|
||||
expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1)
|
||||
|
||||
// plural exported
|
||||
expIdentPlural: #expIdent | *"\(expIdent)s"
|
||||
|
||||
...
|
||||
}
|
||||
Reference in New Issue
Block a user