1
0
mirror of https://github.com/rancher/norman.git synced 2025-09-03 16:25:09 +00:00

Enable dynamic schemas

This commit is contained in:
Darren Shepherd
2017-12-16 01:25:28 -07:00
parent 480747082c
commit 627e06e37f
3 changed files with 175 additions and 51 deletions

View File

@@ -26,7 +26,7 @@ type Server struct {
Resolver parse.ResolverFunc
SubContextAttributeProvider types.SubContextAttributeProvider
ResponseWriters map[string]ResponseWriter
schemas *types.Schemas
Schemas *types.Schemas
QueryFilter types.QueryFilter
StoreWrapper StoreWrapper
URLParser parse.URLParser
@@ -46,7 +46,7 @@ type Defaults struct {
func NewAPIServer() *Server {
s := &Server{
schemas: types.NewSchemas(),
Schemas: types.NewSchemas(),
ResponseWriters: map[string]ResponseWriter{
"json": &writer.JSONResponseWriter{},
"html": &writer.HTMLResponseWriter{},
@@ -68,12 +68,13 @@ func NewAPIServer() *Server {
QueryFilter: handler.QueryFilter,
}
s.Schemas.AddHook = s.setupDefaults
s.Parser = s.parser
return s
}
func (s *Server) parser(rw http.ResponseWriter, req *http.Request) (*types.APIContext, error) {
ctx, err := parse.Parse(rw, req, s.schemas, s.URLParser, s.Resolver)
ctx, err := parse.Parse(rw, req, s.Schemas, s.URLParser, s.Resolver)
ctx.ResponseWriter = s.ResponseWriters[ctx.ResponseFormat]
if ctx.ResponseWriter == nil {
ctx.ResponseWriter = s.ResponseWriters["json"]
@@ -102,20 +103,15 @@ func (s *Server) AddSchemas(schemas *types.Schemas) error {
return
}
for _, schema := range builtin.Schemas.Schemas() {
s.addSchema(*schema)
s.Schemas.AddSchema(*schema)
}
})
for _, schema := range schemas.Schemas() {
s.addSchema(*schema)
s.Schemas.AddSchema(*schema)
}
return s.schemas.Err()
}
func (s *Server) addSchema(schema types.Schema) {
s.setupDefaults(&schema)
s.schemas.AddSchema(schema)
return s.Schemas.Err()
}
func (s *Server) setupDefaults(schema *types.Schema) {

View File

@@ -5,6 +5,8 @@ import (
"fmt"
"strings"
"sync"
"github.com/rancher/norman/name"
"github.com/rancher/norman/types/convert"
"github.com/rancher/norman/types/definition"
@@ -14,7 +16,9 @@ type SchemaCollection struct {
Data []Schema
}
type SchemaInitFunc func(*Schemas) *Schemas
type SchemasInitFunc func(*Schemas) *Schemas
type SchemaHook func(*Schema)
type MappersFactory func() []Mapper
@@ -24,14 +28,17 @@ type BackReference struct {
}
type Schemas struct {
sync.Mutex
schemasByPath map[string]map[string]*Schema
schemasBySubContext map[string]*Schema
mappers map[string]map[string][]Mapper
references map[string][]BackReference
embedded map[string]*Schema
DefaultMappers MappersFactory
DefaultPostMappers MappersFactory
versions []APIVersion
schemas []*Schema
AddHook SchemaHook
errors []error
}
@@ -41,10 +48,11 @@ func NewSchemas() *Schemas {
schemasBySubContext: map[string]*Schema{},
mappers: map[string]map[string][]Mapper{},
references: map[string][]BackReference{},
embedded: map[string]*Schema{},
}
}
func (s *Schemas) Init(initFunc SchemaInitFunc) *Schemas {
func (s *Schemas) Init(initFunc SchemasInitFunc) *Schemas {
return initFunc(s)
}
@@ -53,13 +61,11 @@ func (s *Schemas) Err() error {
}
func (s *Schemas) SubContext(subContext string) *Schema {
s.Lock()
defer s.Unlock()
return s.schemasBySubContext[subContext]
}
func (s *Schemas) SubContextSchemas() map[string]*Schema {
return s.schemasBySubContext
}
func (s *Schemas) AddSchemas(schema *Schemas) *Schemas {
for _, schema := range schema.Schemas() {
s.AddSchema(*schema)
@@ -67,27 +73,58 @@ func (s *Schemas) AddSchemas(schema *Schemas) *Schemas {
return s
}
func (s *Schemas) RemoveSchema(schema Schema) *Schemas {
s.Lock()
defer s.Unlock()
return s.doRemoveSchema(schema)
}
func (s *Schemas) doRemoveSchema(schema Schema) *Schemas {
delete(s.schemasByPath[schema.Version.Path], schema.ID)
s.removeReferences(&schema)
delete(s.schemasBySubContext, schema.SubContext)
if schema.Embed {
s.removeEmbed(&schema)
}
return s
}
func (s *Schemas) removeReferences(schema *Schema) {
fullType := convert.ToFullReference(schema.Version.Path, schema.ID)
delete(s.references, fullType)
for name, values := range s.references {
changed := false
var modified []BackReference
for _, value := range values {
if value.Schema.ID == schema.ID && value.Schema.Version.Path == schema.Version.Path {
changed = true
continue
}
modified = append(modified, value)
}
if changed {
s.references[name] = modified
}
}
}
func (s *Schemas) AddSchema(schema Schema) *Schemas {
schema.Type = "/meta/schemas/schema"
if schema.ID == "" {
s.errors = append(s.errors, fmt.Errorf("ID is not set on schema: %v", schema))
return s
s.Lock()
defer s.Unlock()
return s.doAddSchema(schema)
}
if schema.Version.Path == "" || schema.Version.Version == "" {
s.errors = append(s.errors, fmt.Errorf("version is not set on schema: %s", schema.ID))
return s
}
if schema.PluralName == "" {
schema.PluralName = name.GuessPluralName(schema.ID)
}
if schema.CodeName == "" {
schema.CodeName = convert.Capitalize(schema.ID)
}
if schema.CodeNamePlural == "" {
schema.CodeNamePlural = name.GuessPluralName(schema.CodeName)
}
if schema.BaseType == "" {
schema.BaseType = schema.ID
func (s *Schemas) doAddSchema(schema Schema) *Schemas {
s.setupDefaults(&schema)
if s.AddHook != nil {
s.AddHook(&schema)
}
schemas, ok := s.schemasByPath[schema.Version.Path]
@@ -101,6 +138,64 @@ func (s *Schemas) AddSchema(schema Schema) *Schemas {
schemas[schema.ID] = &schema
s.schemas = append(s.schemas, &schema)
if !schema.Embed {
s.addReferences(&schema)
}
}
if schema.SubContext != "" {
s.schemasBySubContext[schema.SubContext] = &schema
}
if schema.Embed {
s.embed(&schema)
}
return s
}
func (s *Schemas) removeEmbed(schema *Schema) {
target := s.doSchema(&schema.Version, schema.EmbedType, false)
if target == nil {
return
}
newSchema := *target
newSchema.ResourceFields = map[string]Field{}
for k, v := range target.ResourceFields {
newSchema.ResourceFields[k] = v
}
for k := range schema.ResourceFields {
delete(newSchema.ResourceFields, k)
}
s.doRemoveSchema(*target)
s.doAddSchema(newSchema)
}
func (s *Schemas) embed(schema *Schema) {
target := s.doSchema(&schema.Version, schema.EmbedType, false)
if target == nil {
return
}
newSchema := *target
newSchema.ResourceFields = map[string]Field{}
for k, v := range target.ResourceFields {
newSchema.ResourceFields[k] = v
}
for k, v := range schema.ResourceFields {
newSchema.ResourceFields[k] = v
}
s.doRemoveSchema(*target)
s.doAddSchema(newSchema)
}
func (s *Schemas) addReferences(schema *Schema) {
for name, field := range schema.ResourceFields {
if !definition.IsReferenceType(field.Type) {
continue
@@ -113,20 +208,39 @@ func (s *Schemas) AddSchema(schema Schema) *Schemas {
s.references[refType] = append(s.references[refType], BackReference{
FieldName: name,
Schema: &schema,
Schema: schema,
})
}
}
if schema.SubContext != "" {
s.schemasBySubContext[schema.SubContext] = &schema
func (s *Schemas) setupDefaults(schema *Schema) {
schema.Type = "/meta/schemas/schema"
if schema.ID == "" {
s.errors = append(s.errors, fmt.Errorf("ID is not set on schema: %v", schema))
return
}
if schema.Version.Path == "" || schema.Version.Version == "" {
s.errors = append(s.errors, fmt.Errorf("version is not set on schema: %s", schema.ID))
return
}
if schema.PluralName == "" {
schema.PluralName = name.GuessPluralName(schema.ID)
}
if schema.CodeName == "" {
schema.CodeName = convert.Capitalize(schema.ID)
}
if schema.CodeNamePlural == "" {
schema.CodeNamePlural = name.GuessPluralName(schema.CodeName)
}
if schema.BaseType == "" {
schema.BaseType = schema.ID
}
return s
}
func (s *Schemas) References(schema *Schema) []BackReference {
refType := convert.ToFullReference(schema.Version.Path, schema.ID)
s.Lock()
defer s.Unlock()
return s.references[refType]
}
@@ -142,6 +256,8 @@ func (s *Schemas) AddMapper(version *APIVersion, schemaID string, mapper Mapper)
}
func (s *Schemas) SchemasForVersion(version APIVersion) map[string]*Schema {
s.Lock()
defer s.Unlock()
return s.schemasByPath[version.Path]
}
@@ -182,6 +298,10 @@ func (s *Schemas) mapper(version *APIVersion, name string) []Mapper {
}
func (s *Schemas) Schema(version *APIVersion, name string) *Schema {
return s.doSchema(version, name, true)
}
func (s *Schemas) doSchema(version *APIVersion, name string, lock bool) *Schema {
var (
path string
)
@@ -196,7 +316,13 @@ func (s *Schemas) Schema(version *APIVersion, name string) *Schema {
path = "core"
}
if lock {
s.Lock()
}
schemas, ok := s.schemasByPath[path]
if lock {
s.Unlock()
}
if !ok {
return nil
}

View File

@@ -79,6 +79,8 @@ type TypeScope string
type Schema struct {
ID string `json:"id,omitempty"`
Embed bool `json:"embed,omitempty"`
EmbedType string `json:"embedType,omitempty"`
CodeName string `json:"-"`
CodeNamePlural string `json:"-"`
PkgName string `json:"-"`