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:
@@ -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 != "" {
|
||||||
|
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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:]...)
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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{}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user