Define a common pkg/data for model definitions
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package data
|
||||
|
||||
type (
|
||||
modelDiffType string
|
||||
|
||||
// ModelDiff defines one identified missmatch between two models
|
||||
ModelDiff struct {
|
||||
Type modelDiffType
|
||||
Original *Attribute
|
||||
Asserted *Attribute
|
||||
}
|
||||
|
||||
ModelDiffSet []*ModelDiff
|
||||
)
|
||||
|
||||
const (
|
||||
AttributeMissing modelDiffType = "attributeMissing"
|
||||
AttributeTypeMissmatch modelDiffType = "typeMissmatch"
|
||||
)
|
||||
|
||||
// Diff calculates the diff between models a and b where a is used as base
|
||||
func (a *Model) Diff(b *Model) (out ModelDiffSet) {
|
||||
bIndex := make(map[string]struct {
|
||||
found bool
|
||||
attr *Attribute
|
||||
})
|
||||
for _, _attr := range b.Attributes {
|
||||
attr := _attr
|
||||
bIndex[attr.Ident] = struct {
|
||||
found bool
|
||||
attr *Attribute
|
||||
}{
|
||||
attr: attr,
|
||||
}
|
||||
}
|
||||
|
||||
for _, _attrA := range a.Attributes {
|
||||
attrA := _attrA
|
||||
|
||||
// Missmatches
|
||||
attrBAux, ok := bIndex[attrA.Ident]
|
||||
if !ok {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeMissing,
|
||||
Original: attrA,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Typecheck
|
||||
if attrA.Type.Type() != attrBAux.attr.Type.Type() {
|
||||
out = append(out, &ModelDiff{
|
||||
Type: AttributeTypeMissmatch,
|
||||
Original: attrA,
|
||||
Asserted: attrBAux.attr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
"github.com/cortezaproject/corteza-server/pkg/minions"
|
||||
)
|
||||
|
||||
type (
|
||||
// Model describes the underlying data and its shape
|
||||
Model struct {
|
||||
StoreID uint64
|
||||
Ident string
|
||||
|
||||
ResourceID uint64
|
||||
ResourceType string
|
||||
|
||||
Attributes AttributeSet
|
||||
}
|
||||
ModelSet []*Model
|
||||
|
||||
// Attribute describes a specific value of the dataset
|
||||
Attribute struct {
|
||||
Ident string
|
||||
|
||||
MultiValue bool
|
||||
|
||||
// Store describes the strategy the underlying storage system should
|
||||
// apply to the underlying value
|
||||
Store StoreStrategy
|
||||
// Type describes what the value represents and how it should be
|
||||
// encoded/decoded
|
||||
Type attributeType
|
||||
}
|
||||
AttributeSet []*Attribute
|
||||
|
||||
AttributeType string
|
||||
attributeType interface {
|
||||
Type() AttributeType
|
||||
}
|
||||
|
||||
// TypeID handles ID (uint64) coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeID struct {
|
||||
// @todo need to figure out how to support when IDs
|
||||
// generated/provided by store (SERIAL/AUTOINCREMENT)
|
||||
GeneratedByStore bool
|
||||
}
|
||||
|
||||
// TypeRef handles ID (uint64) coding + reference info
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeRef struct {
|
||||
RefModel *Model
|
||||
RefAttribute *Attribute
|
||||
}
|
||||
|
||||
// TypeTimestamp handles timestamp coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeTimestamp struct{}
|
||||
|
||||
// TypeTime handles time coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeTime struct{}
|
||||
|
||||
// TypeDate handles date coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeDate struct{}
|
||||
|
||||
// TypeNumber handles number coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeNumber struct {
|
||||
Precision uint
|
||||
Scale uint
|
||||
}
|
||||
|
||||
// TypeText handles string coding
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeText struct{ Length uint }
|
||||
|
||||
// TypeBoolean
|
||||
TypeBoolean struct{}
|
||||
|
||||
// TypeEnum
|
||||
TypeEnum struct {
|
||||
Values []string
|
||||
}
|
||||
|
||||
// TypeGeometry
|
||||
TypeGeometry struct{}
|
||||
|
||||
// TypeJSON handles coding of arbitrary data into JSON structure
|
||||
// NOT TO BE CONFUSED with encodedField
|
||||
//
|
||||
// Encoding/decoding might be different depending on
|
||||
// 1) underlying store (and dialect)
|
||||
// 2) value codec (raw, json ...)
|
||||
TypeJSON struct{}
|
||||
|
||||
// TypeBlob store/return data as
|
||||
TypeBlob struct{}
|
||||
|
||||
TypeUUID struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
typeID AttributeType = "id"
|
||||
typeRef AttributeType = "ref"
|
||||
typeTimestamp AttributeType = "timestamp"
|
||||
typeTime AttributeType = "time"
|
||||
typeDate AttributeType = "date"
|
||||
typeNumber AttributeType = "number"
|
||||
typeText AttributeType = "text"
|
||||
typeBoolean AttributeType = "boolean"
|
||||
typeEnum AttributeType = "enum"
|
||||
typeGeometry AttributeType = "geometry"
|
||||
typeJSON AttributeType = "json"
|
||||
typeBlob AttributeType = "blob"
|
||||
typeUUID AttributeType = "uuid"
|
||||
)
|
||||
|
||||
// FindByIdent returns the model that matches the ident
|
||||
func (mm ModelSet) FindByIdent(ident string) *Model {
|
||||
for _, m := range mm {
|
||||
if m.Ident == ident {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterByReferenced returns all of the models that reference b
|
||||
func (aa ModelSet) FilterByReferenced(b *Model) (out ModelSet) {
|
||||
for _, aModel := range aa {
|
||||
if aModel.Ident == b.Ident {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, aAttribute := range aModel.Attributes {
|
||||
switch casted := aAttribute.Type.(type) {
|
||||
case TypeRef:
|
||||
if casted.RefModel.Ident == b.Ident {
|
||||
out = append(out, aModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HasAttribute returns true when the model includes the specified ident
|
||||
func (m Model) HasAttribute(ident string) bool {
|
||||
return m.Attributes.FindByIdent(ident) != nil
|
||||
}
|
||||
|
||||
func (aa AttributeSet) FindByIdent(ident string) *Attribute {
|
||||
for _, a := range aa {
|
||||
if a.Ident == ident {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate performs a base model validation before it is passed down
|
||||
func (m Model) Validate() error {
|
||||
if m.Ident == "" {
|
||||
return fmt.Errorf("ident not defined")
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
for _, attr := range m.Attributes {
|
||||
if attr.Ident == "" {
|
||||
return fmt.Errorf("invalid attribute ident: ident must not be empty")
|
||||
}
|
||||
|
||||
if !handle.IsValid(attr.Ident) {
|
||||
return fmt.Errorf("invalid attribute ident: %s is not a valid handle", attr.Ident)
|
||||
}
|
||||
|
||||
if seen[attr.Ident] {
|
||||
return fmt.Errorf("invalid attribute %s: duplicate attributes are not allowed", attr.Ident)
|
||||
}
|
||||
seen[attr.Ident] = true
|
||||
|
||||
if minions.IsNil(attr.Type) {
|
||||
return fmt.Errorf("attribute does not define a type: %s", attr.Ident)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Receivers to conform to the interface
|
||||
|
||||
func (t TypeID) Type() AttributeType {
|
||||
return typeID
|
||||
}
|
||||
|
||||
func (t TypeRef) Type() AttributeType {
|
||||
return typeRef
|
||||
}
|
||||
|
||||
func (t TypeTimestamp) Type() AttributeType {
|
||||
return typeTimestamp
|
||||
}
|
||||
|
||||
func (t TypeTime) Type() AttributeType {
|
||||
return typeTime
|
||||
}
|
||||
|
||||
func (t TypeDate) Type() AttributeType {
|
||||
return typeDate
|
||||
}
|
||||
|
||||
func (t TypeNumber) Type() AttributeType {
|
||||
return typeNumber
|
||||
}
|
||||
|
||||
func (t TypeText) Type() AttributeType {
|
||||
return typeText
|
||||
}
|
||||
|
||||
func (t TypeBoolean) Type() AttributeType {
|
||||
return typeBoolean
|
||||
}
|
||||
|
||||
func (t TypeEnum) Type() AttributeType {
|
||||
return typeEnum
|
||||
}
|
||||
|
||||
func (t TypeGeometry) Type() AttributeType {
|
||||
return typeGeometry
|
||||
}
|
||||
|
||||
func (t TypeJSON) Type() AttributeType {
|
||||
return typeJSON
|
||||
}
|
||||
|
||||
func (t TypeBlob) Type() AttributeType {
|
||||
return typeBlob
|
||||
}
|
||||
|
||||
func (t TypeUUID) Type() AttributeType {
|
||||
return typeUUID
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package data
|
||||
|
||||
type (
|
||||
// StoreStrategy defines how values for a specific model attribute
|
||||
// are retrieved or stored
|
||||
//
|
||||
// If attribute does not have store strategy set
|
||||
// store driver should use attribute name to determinate
|
||||
// source/destination of the value (table column, json doc property)
|
||||
StoreStrategy any
|
||||
|
||||
// StoreCodecJSON defines that values are encoded/decoded into
|
||||
// a JSON document (stored under ident)
|
||||
// Path defines exact location inside the doc.
|
||||
StoreCodecJSON struct {
|
||||
Ident string
|
||||
Path []any
|
||||
}
|
||||
|
||||
// { "@value": ... "@type": .... }
|
||||
// StoreCodecJSONLD struct { Ident string; Path []any }
|
||||
|
||||
// StoreCodecXML
|
||||
//StoreCodecXML struct {}
|
||||
|
||||
// StoreCodecAlias defines case when value is not stored
|
||||
// under the same column (in case of an SQL table)
|
||||
//
|
||||
// Example: attribute ident is "foo" and database table column is "bar"
|
||||
StoreCodecAlias struct {
|
||||
Ident string
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user