github.com/Axway/agent-sdk@v1.1.101/pkg/apic/provisioning/schemabuilder.go (about)

     1  package provisioning
     2  
     3  import (
     4  	"encoding/json"
     5  	"sort"
     6  
     7  	"github.com/Axway/agent-sdk/pkg/util/log"
     8  )
     9  
    10  // SchemaBuilder - used to build a subscription schema for API Central
    11  type SchemaParser interface {
    12  	Parse(schemaBytes []byte) (map[string]PropertyDefinition, error)
    13  }
    14  
    15  type schemaParser struct {
    16  }
    17  
    18  // NewSchemaBuilder - Creates a new subscription schema builder
    19  func NewSchemaParser() SchemaParser {
    20  	return &schemaParser{}
    21  }
    22  
    23  // SchemaBuilder - used to build a subscription schema for API Central
    24  type SchemaBuilder interface {
    25  	SetName(name string) SchemaBuilder
    26  	SetDescription(description string) SchemaBuilder
    27  	SetPropertyOrder(propertyOrder []string) SchemaBuilder
    28  	AddProperty(property PropertyBuilder) SchemaBuilder
    29  	AddUniqueKey(keyName string) SchemaBuilder
    30  	// Build builds the json schema - this is called automatically by the resource builder
    31  	Build() (map[string]interface{}, error)
    32  }
    33  
    34  // schemaBuilder - holds all the details needs to create a subscription schema
    35  type schemaBuilder struct {
    36  	err              error
    37  	name             string
    38  	description      string
    39  	propertyOrder    []string
    40  	uniqueKeys       []string
    41  	properties       map[string]propertyDefinition
    42  	dependencies     map[string]*oneOfPropertyDefinitions
    43  	schemaVersion    string
    44  	propertyOrderSet bool
    45  }
    46  
    47  // jsonSchema - the schema generated from the builder
    48  type jsonSchema struct {
    49  	SubscriptionName  string                               `json:"-"`
    50  	SchemaType        string                               `json:"type"`
    51  	SchemaVersion     string                               `json:"$schema"`
    52  	SchemaDescription string                               `json:"description"`
    53  	Properties        map[string]propertyDefinition        `json:"properties"`
    54  	Dependencies      map[string]*oneOfPropertyDefinitions `json:"dependencies,omitempty"`
    55  	PropertyOrder     []string                             `json:"x-axway-order,omitempty"`
    56  	Required          []string                             `json:"required,omitempty"`
    57  }
    58  
    59  // NewSchemaBuilder - Creates a new subscription schema builder
    60  func NewSchemaBuilder() SchemaBuilder {
    61  	return &schemaBuilder{
    62  		properties:       make(map[string]propertyDefinition, 0),
    63  		dependencies:     make(map[string]*oneOfPropertyDefinitions),
    64  		uniqueKeys:       make([]string, 0),
    65  		propertyOrder:    make([]string, 0),
    66  		propertyOrderSet: false,
    67  		schemaVersion:    "http://json-schema.org/draft-07/schema#",
    68  	}
    69  }
    70  
    71  // SetName - give the subscription schema a name
    72  func (s *schemaBuilder) SetName(name string) SchemaBuilder {
    73  	s.name = name
    74  	return s
    75  }
    76  
    77  // SetDescription - give the subscription schema a description
    78  func (s *schemaBuilder) SetDescription(description string) SchemaBuilder {
    79  	s.description = description
    80  	return s
    81  }
    82  
    83  // SetPropertyOrder - Set a list of ordered fields to be rendered in the UI
    84  func (s *schemaBuilder) SetPropertyOrder(propertyOrder []string) SchemaBuilder {
    85  	// If property names in the property order is bogus, it will be ignored when rendered
    86  	s.propertyOrder = propertyOrder
    87  	s.propertyOrderSet = true
    88  	return s
    89  }
    90  
    91  // AddProperty - adds a new subscription schema property to the schema
    92  func (s *schemaBuilder) AddProperty(property PropertyBuilder) SchemaBuilder {
    93  	prop, err := property.Build()
    94  	if err == nil {
    95  		s.properties[prop.Name] = *prop
    96  
    97  		// If property order wasn't set, add property as they come in
    98  		if !s.propertyOrderSet {
    99  			s.propertyOrder = append(s.propertyOrder, prop.Name)
   100  		}
   101  
   102  		dep, err := property.BuildDependencies()
   103  		if err != nil {
   104  			s.err = err
   105  		}
   106  		if dep != nil {
   107  			s.dependencies[prop.Name] = dep
   108  		}
   109  	} else {
   110  		s.err = err
   111  	}
   112  
   113  	return s
   114  }
   115  
   116  // inList - check to see if the string is in the list.
   117  func inList(value string, list []string) bool {
   118  	for _, v := range list {
   119  		if v == value {
   120  			return true
   121  		}
   122  	}
   123  	return false
   124  }
   125  
   126  // AddUniqueKey - add a unique key to the schema
   127  func (s *schemaBuilder) AddUniqueKey(keyName string) SchemaBuilder {
   128  	s.uniqueKeys = append(s.uniqueKeys, keyName)
   129  	return s
   130  }
   131  
   132  // Register - build and register the subscription schema
   133  func (s *schemaBuilder) Build() (map[string]interface{}, error) {
   134  	if s.err != nil {
   135  		return nil, s.err
   136  	}
   137  
   138  	// validate that the property added is in the property order set by the implementation
   139  	for _, value := range s.properties {
   140  		if len(s.propertyOrder) > 0 {
   141  			// if property is not in the set property order, warn
   142  			if !inList(value.Name, s.propertyOrder) {
   143  				log.Warnf("property %s is not found in the property order", value.Name)
   144  			}
   145  		}
   146  	}
   147  
   148  	// validate that the properties in the property order were added
   149  	// and that all props in property order are only in once
   150  	if len(s.propertyOrder) > 0 {
   151  		newOrder := []string{}
   152  		props := map[string]struct{}{}
   153  		for _, orderedProperty := range s.propertyOrder {
   154  			if _, ok := s.properties[orderedProperty]; !ok {
   155  				log.Warnf("ordered property %s, was not added as a property", orderedProperty)
   156  			}
   157  
   158  			if _, ok := props[orderedProperty]; !ok {
   159  				newOrder = append(newOrder, orderedProperty)
   160  				props[orderedProperty] = struct{}{}
   161  			}
   162  		}
   163  		s.propertyOrder = newOrder
   164  	}
   165  
   166  	// Create the list of required properties
   167  	required := make([]string, 0)
   168  	for key, value := range s.properties {
   169  		if value.Required {
   170  			required = append(required, key)
   171  		}
   172  	}
   173  	sort.Strings(required)
   174  	schema := &jsonSchema{
   175  		SubscriptionName:  s.name,
   176  		SchemaType:        "object",
   177  		SchemaVersion:     s.schemaVersion,
   178  		SchemaDescription: s.description,
   179  		Properties:        s.properties,
   180  		Dependencies:      s.dependencies,
   181  		PropertyOrder:     s.propertyOrder,
   182  		Required:          required,
   183  	}
   184  
   185  	schemaBytes, err := json.Marshal(schema)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	schemaMap := map[string]interface{}{}
   190  	err = json.Unmarshal(schemaBytes, &schemaMap)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	return schemaMap, nil
   196  }
   197  
   198  func (s *schemaParser) Parse(schemaBytes []byte) (map[string]PropertyDefinition, error) {
   199  	schema := &jsonSchema{}
   200  	err := json.Unmarshal(schemaBytes, schema)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	ret := make(map[string]PropertyDefinition)
   206  	for s, p := range schema.Properties {
   207  		buf, _ := json.Marshal(p)
   208  		newprop := &propertyDefinition{}
   209  		json.Unmarshal(buf, newprop)
   210  		ret[s] = newprop
   211  	}
   212  	return ret, nil
   213  }