1
0
mirror of https://github.com/rancher/norman.git synced 2025-09-09 02:59:19 +00:00

Generate backreference links

This commit is contained in:
Darren Shepherd
2017-12-13 08:53:28 -07:00
parent d0aef1eafd
commit 9dcef33943
6 changed files with 60 additions and 52 deletions

View File

@@ -3,7 +3,6 @@ package builtin
import ( import (
"github.com/rancher/norman/store/empty" "github.com/rancher/norman/store/empty"
"github.com/rancher/norman/types" "github.com/rancher/norman/types"
"github.com/rancher/norman/types/convert"
) )
func APIRootFormatter(apiContext *types.APIContext, resource *types.RawResource) { func APIRootFormatter(apiContext *types.APIContext, resource *types.RawResource) {
@@ -21,22 +20,6 @@ func APIRootFormatter(apiContext *types.APIContext, resource *types.RawResource)
resource.Links["self"] = apiContext.URLBuilder.Version(apiVersion) resource.Links["self"] = apiContext.URLBuilder.Version(apiVersion)
if len(apiVersion.SubContexts) > 0 {
subContextToSchema := apiContext.Schemas.SubContextSchemas()
if len(subContextToSchema) > 0 {
for _, schema := range subContextToSchema {
addCollectionLink(apiContext, schema, resource.Links)
}
for _, schema := range getNonReferencedSchemas(apiContext.Schemas.SchemasForVersion(apiVersion),
subContextToSchema) {
addCollectionLink(apiContext, schema, resource.Links)
}
return
}
}
for _, schema := range apiContext.Schemas.SchemasForVersion(apiVersion) { for _, schema := range apiContext.Schemas.SchemasForVersion(apiVersion) {
addCollectionLink(apiContext, schema, resource.Links) addCollectionLink(apiContext, schema, resource.Links)
} }
@@ -44,31 +27,6 @@ func APIRootFormatter(apiContext *types.APIContext, resource *types.RawResource)
return return
} }
func getNonReferencedSchemas(schemas map[string]*types.Schema, subContexts map[string]*types.Schema) []*types.Schema {
var result []*types.Schema
typeNames := map[string]bool{}
for _, subContext := range subContexts {
ref := convert.ToReference(subContext.ID)
fullRef := convert.ToFullReference(subContext.Version.Path, subContext.ID)
typeNames[ref] = true
typeNames[fullRef] = true
}
outer:
for _, schema := range schemas {
for _, field := range schema.ResourceFields {
if typeNames[field.Type] {
continue outer
}
}
result = append(result, schema)
}
return result
}
func addCollectionLink(apiContext *types.APIContext, schema *types.Schema, links map[string]string) { func addCollectionLink(apiContext *types.APIContext, schema *types.Schema, links map[string]string) {
collectionLink := getSchemaCollectionLink(apiContext, schema, nil) collectionLink := getSchemaCollectionLink(apiContext, schema, nil)
if collectionLink != "" { if collectionLink != "" {

View File

@@ -121,14 +121,24 @@ func (j *JSONResponseWriter) convert(b *builder.Builder, context *types.APIConte
} }
func (j *JSONResponseWriter) addLinks(b *builder.Builder, schema *types.Schema, context *types.APIContext, input map[string]interface{}, rawResource *types.RawResource) { func (j *JSONResponseWriter) addLinks(b *builder.Builder, schema *types.Schema, context *types.APIContext, input map[string]interface{}, rawResource *types.RawResource) {
if rawResource.ID != "" { if rawResource.ID == "" {
self := context.URLBuilder.ResourceLink(rawResource) return
rawResource.Links["self"] = self }
if schema.CanUpdate() {
rawResource.Links["update"] = self self := context.URLBuilder.ResourceLink(rawResource)
} rawResource.Links["self"] = self
if schema.CanDelete() { if schema.CanUpdate() {
rawResource.Links["remove"] = self rawResource.Links["update"] = self
}
if schema.CanDelete() {
rawResource.Links["remove"] = self
}
for _, backRef := range context.Schemas.References(schema) {
if schema.SubContext == "" {
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.FilterLink(backRef.Schema, backRef.FieldName, rawResource.ID)
} else {
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.SubContextCollection(schema, rawResource.ID, backRef.Schema)
} }
} }
} }

View File

@@ -50,7 +50,7 @@ func DefaultURLParser(schemas *types.Schemas, url *url.URL) (ParsedURL, error) {
path = multiSlashRegexp.ReplaceAllString(path, "/") path = multiSlashRegexp.ReplaceAllString(path, "/")
parts := strings.SplitN(path[len(version.Path):], "/", 4) parts := strings.SplitN(path[len(version.Path):], "/", 4)
prefix, parts, subContext := parseSubContext(version, parts) prefix, parts, subContext := parseSubContext(schemas, version, parts)
result.Version = version.Path result.Version = version.Path
result.SubContext = subContext result.SubContext = subContext
@@ -139,7 +139,7 @@ func Parse(rw http.ResponseWriter, req *http.Request, schemas *types.Schemas, ur
return result, nil return result, nil
} }
func parseSubContext(version *types.APIVersion, parts []string) (string, []string, map[string]string) { func parseSubContext(schemas *types.Schemas, version *types.APIVersion, parts []string) (string, []string, map[string]string) {
subContext := "" subContext := ""
result := map[string]string{} result := map[string]string{}
@@ -151,6 +151,11 @@ func parseSubContext(version *types.APIVersion, parts []string) (string, []strin
break break
} }
subSchema := schemas.Schema(version, parts[3])
if subSchema == nil {
break
}
result[resourceType] = resourceID result[resourceType] = resourceID
subContext = subContext + "/" + resourceType + "/" + resourceID subContext = subContext + "/" + resourceType + "/" + resourceID
parts = append(parts[:1], parts[3:]...) parts = append(parts[:1], parts[3:]...)

View File

@@ -7,6 +7,7 @@ import (
"github.com/rancher/norman/name" "github.com/rancher/norman/name"
"github.com/rancher/norman/types/convert" "github.com/rancher/norman/types/convert"
"github.com/rancher/norman/types/definition"
) )
type SchemaCollection struct { type SchemaCollection struct {
@@ -17,10 +18,16 @@ type SchemaInitFunc func(*Schemas) *Schemas
type MappersFactory func() []Mapper type MappersFactory func() []Mapper
type BackReference struct {
FieldName string
Schema *Schema
}
type Schemas struct { type Schemas struct {
schemasByPath map[string]map[string]*Schema schemasByPath map[string]map[string]*Schema
schemasBySubContext map[string]*Schema schemasBySubContext map[string]*Schema
mappers map[string]map[string][]Mapper mappers map[string]map[string][]Mapper
references map[string][]BackReference
DefaultMappers MappersFactory DefaultMappers MappersFactory
DefaultPostMappers MappersFactory DefaultPostMappers MappersFactory
versions []APIVersion versions []APIVersion
@@ -33,6 +40,7 @@ func NewSchemas() *Schemas {
schemasByPath: map[string]map[string]*Schema{}, schemasByPath: map[string]map[string]*Schema{},
schemasBySubContext: map[string]*Schema{}, schemasBySubContext: map[string]*Schema{},
mappers: map[string]map[string][]Mapper{}, mappers: map[string]map[string][]Mapper{},
references: map[string][]BackReference{},
} }
} }
@@ -92,6 +100,22 @@ func (s *Schemas) AddSchema(schema Schema) *Schemas {
if _, ok := schemas[schema.ID]; !ok { if _, ok := schemas[schema.ID]; !ok {
schemas[schema.ID] = &schema schemas[schema.ID] = &schema
s.schemas = append(s.schemas, &schema) s.schemas = append(s.schemas, &schema)
for name, field := range schema.ResourceFields {
if !definition.IsReferenceType(field.Type) {
continue
}
refType := definition.SubType(field.Type)
if !strings.HasPrefix(refType, "/") {
refType = convert.ToFullReference(schema.Version.Path, refType)
}
s.references[refType] = append(s.references[refType], BackReference{
FieldName: name,
Schema: &schema,
})
}
} }
if schema.SubContext != "" { if schema.SubContext != "" {
@@ -101,6 +125,11 @@ func (s *Schemas) AddSchema(schema Schema) *Schemas {
return s return s
} }
func (s *Schemas) References(schema *Schema) []BackReference {
refType := convert.ToFullReference(schema.Version.Path, schema.ID)
return s.references[refType]
}
func (s *Schemas) AddMapper(version *APIVersion, schemaID string, mapper Mapper) *Schemas { func (s *Schemas) AddMapper(version *APIVersion, schemaID string, mapper Mapper) *Schemas {
mappers, ok := s.mappers[version.Path] mappers, ok := s.mappers[version.Path]
if !ok { if !ok {

View File

@@ -147,6 +147,7 @@ type URLBuilder interface {
ReverseSort(order SortOrder) string ReverseSort(order SortOrder) string
Sort(field string) string Sort(field string) string
SetSubContext(subContext string) SetSubContext(subContext string)
FilterLink(schema *Schema, fieldName string, value string) string
} }
type Store interface { type Store interface {

View File

@@ -129,6 +129,11 @@ func (u *urlBuilder) Version(version types.APIVersion) string {
return u.constructBasicURL(version) return u.constructBasicURL(version)
} }
func (u *urlBuilder) FilterLink(schema *types.Schema, fieldName string, value string) string {
return u.constructBasicURL(schema.Version, schema.PluralName) + "?" +
url.QueryEscape(fieldName) + "=" + url.QueryEscape(value)
}
func (u *urlBuilder) constructBasicURL(version types.APIVersion, parts ...string) string { func (u *urlBuilder) constructBasicURL(version types.APIVersion, parts ...string) string {
buffer := bytes.Buffer{} buffer := bytes.Buffer{}