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

     1  package provisioning
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  )
     7  
     8  // Supported data types
     9  const (
    10  	DataTypeString  = "string"
    11  	DataTypeNumber  = "number"
    12  	DataTypeInteger = "integer"
    13  	DataTypeArray   = "array"
    14  	DataTypeObject  = "object"
    15  )
    16  
    17  // oneOfPropertyDefinitions - used for items of propertyDefinition
    18  type oneOfPropertyDefinitions struct {
    19  	OneOf []*propertyDefinition `json:"oneOf,omitempty"`
    20  }
    21  
    22  // anyOfPropertyDefinitions - used for items of propertyDefinition
    23  type anyOfPropertyDefinitions struct {
    24  	AnyOf []propertyDefinition `json:"anyOf,omitempty"`
    25  }
    26  
    27  type PropertyDefinition interface {
    28  	GetType() string
    29  	GetArrayItems() []PropertyDefinition
    30  	GetEnums() []string
    31  }
    32  
    33  // propertyDefinition -
    34  type propertyDefinition struct {
    35  	Type               string                               `json:"type,omitempty"`
    36  	Title              string                               `json:"title,omitempty"`
    37  	Description        string                               `json:"description,omitempty"`
    38  	Enum               []string                             `json:"enum,omitempty"`
    39  	DefaultValue       interface{}                          `json:"default,omitempty"`
    40  	ReadOnly           bool                                 `json:"readOnly,omitempty"`
    41  	Format             string                               `json:"format,omitempty"`
    42  	Properties         map[string]propertyDefinition        `json:"properties,omitempty"`
    43  	RequiredProperties []string                             `json:"required,omitempty"`
    44  	Dependencies       map[string]*oneOfPropertyDefinitions `json:"dependencies,omitempty"`
    45  	Items              *anyOfPropertyDefinitions            `json:"items,omitempty"`    // We use a pointer to avoid generating an empty struct if not set
    46  	MinItems           *uint                                `json:"minItems,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
    47  	MaxItems           *uint                                `json:"maxItems,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
    48  	Minimum            *float64                             `json:"minimum,omitempty"`  // We use a pointer to differentiate the "blank value" from a chosen 0 min value
    49  	Maximum            *float64                             `json:"maximum,omitempty"`  // We use a pointer to differentiate the "blank value" from a chosen 0 max value
    50  	IsEncrypted        bool                                 `json:"x-axway-encrypted,omitempty"`
    51  	Widget             string                               `json:"x-axway-widget,omitempty"`
    52  	IsCopyable         bool                                 `json:"x-axway-copyable,omitempty"`
    53  	UniqueItems        bool                                 `json:"uniqueItems,omitempty"`
    54  	Name               string                               `json:"-"`
    55  	Required           bool                                 `json:"-"`
    56  }
    57  
    58  func (p *propertyDefinition) GetType() string {
    59  	return p.Type
    60  }
    61  
    62  func (p *propertyDefinition) GetArrayItems() []PropertyDefinition {
    63  	ret := make([]PropertyDefinition, 0)
    64  	if p.Items != nil {
    65  		for _, p := range p.Items.AnyOf {
    66  			ret = append(ret, &p)
    67  		}
    68  	}
    69  	return ret
    70  }
    71  
    72  func (p *propertyDefinition) GetEnums() []string {
    73  	return p.Enum
    74  }
    75  
    76  // PropertyBuilder - mandatory methods for all property builders
    77  type PropertyBuilder interface {
    78  	// Build - builds the property, this is called automatically by the schema builder
    79  	Build() (*propertyDefinition, error)
    80  	// BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
    81  	BuildDependencies() (*oneOfPropertyDefinitions, error)
    82  }
    83  
    84  // TypePropertyBuilder - common methods related to type property builders
    85  type TypePropertyBuilder interface {
    86  	// SetLabel - sets the label for the property
    87  	SetLabel(label string) TypePropertyBuilder
    88  	// SetName - sets the name of the property
    89  	SetName(name string) TypePropertyBuilder
    90  	// SetDescription - set the description of the property
    91  	SetDescription(description string) TypePropertyBuilder
    92  	// SetRequired - set the property as a required field in the schema
    93  	SetRequired() TypePropertyBuilder
    94  	// SetReadOnly - set the property as a read only property
    95  	SetReadOnly() TypePropertyBuilder
    96  	// SetHidden - set the property as a hidden property
    97  	SetHidden() TypePropertyBuilder
    98  	// IsString - Set the property to be of type string
    99  	IsString() StringPropertyBuilder
   100  	// IsInteger - Set the property to be of type integer
   101  	IsInteger() IntegerPropertyBuilder
   102  	// IsNumber - Set the property to be of type number
   103  	IsNumber() NumberPropertyBuilder
   104  	// IsArray - Set the property to be of type array
   105  	IsArray() ArrayPropertyBuilder
   106  	// IsObject - Set the property to be of type object
   107  	IsObject() ObjectPropertyBuilder
   108  	PropertyBuilder
   109  }
   110  
   111  // StringPropertyBuilder - specific methods related to the String property builders
   112  type StringPropertyBuilder interface {
   113  	// SetEnumValues - Set a list of valid values for the property
   114  	SetEnumValues(values []string) StringPropertyBuilder
   115  	// SetSortEnumValues - Sort the allowed values alphabetically in the schema
   116  	SetSortEnumValues() StringPropertyBuilder
   117  	// SetFirstEnumValue - Set the value that should appear first in the list
   118  	SetFirstEnumValue(value string) StringPropertyBuilder
   119  	// AddEnumValue - Add another value to the list of allowed values for the property
   120  	AddEnumValue(value string) StringPropertyBuilder
   121  	// IsEncrypted - Set that this field must be encrypted at rest
   122  	IsEncrypted() StringPropertyBuilder
   123  	// IsCopyable - Set that this field may be copied via the UI
   124  	IsCopyable() StringPropertyBuilder
   125  	// SetDefaultValue - Define the initial value for the property
   126  	SetDefaultValue(value string) StringPropertyBuilder
   127  	// SetAsTextArea - Set value to be rendered as a textarea box within the UI
   128  	SetAsTextArea() StringPropertyBuilder
   129  	// AddDependency - Add property dependencies
   130  	AddDependency(value string, property PropertyBuilder) StringPropertyBuilder
   131  	PropertyBuilder
   132  }
   133  
   134  // NumberPropertyBuilder - specific methods related to the Number property builders
   135  type NumberPropertyBuilder interface {
   136  	// SetMinValue - Set the minimum allowed number value
   137  	SetMinValue(min float64) NumberPropertyBuilder
   138  	// SetMaxValue - Set the maximum allowed number value
   139  	SetMaxValue(min float64) NumberPropertyBuilder
   140  	// SetDefaultValue - Define the initial value for the property
   141  	SetDefaultValue(value float64) NumberPropertyBuilder
   142  	PropertyBuilder
   143  }
   144  
   145  // IntegerPropertyBuilder - specific methods related to the Integer property builders
   146  type IntegerPropertyBuilder interface {
   147  	// SetMinValue - Set the minimum allowed integer value
   148  	SetMinValue(min int64) IntegerPropertyBuilder
   149  	// SetMaxValue - Set the maximum allowed integer value
   150  	SetMaxValue(min int64) IntegerPropertyBuilder
   151  	// SetDefaultValue - Define the initial value for the property
   152  	SetDefaultValue(value int64) IntegerPropertyBuilder
   153  	PropertyBuilder
   154  }
   155  
   156  // ObjectPropertyBuilder - specific methods related to the Object property builders
   157  type ObjectPropertyBuilder interface {
   158  	// AddProperty - Add a property in the object property
   159  	AddProperty(property PropertyBuilder) ObjectPropertyBuilder
   160  	PropertyBuilder
   161  }
   162  
   163  // ArrayPropertyBuilder - specific methods related to the Array property builders
   164  type ArrayPropertyBuilder interface {
   165  	// AddItem - Add an item property in the array property
   166  	AddItem(item PropertyBuilder) ArrayPropertyBuilder
   167  	// SetMinItems - Set the minimum number of items in the array property
   168  	SetMinItems(min uint) ArrayPropertyBuilder
   169  	// SetMaxItems - Set the maximum number of items in the array property
   170  	SetMaxItems(max uint) ArrayPropertyBuilder
   171  	PropertyBuilder
   172  }
   173  
   174  // schemaProperty - holds basic info needed to create a subscription schema property
   175  type schemaProperty struct {
   176  	err         error
   177  	name        string
   178  	label       string
   179  	description string
   180  	required    bool
   181  	readOnly    bool
   182  	hidden      bool
   183  	dataType    string
   184  	PropertyBuilder
   185  }
   186  
   187  // NewSchemaPropertyBuilder - Creates a new subscription schema property builder
   188  func NewSchemaPropertyBuilder() TypePropertyBuilder {
   189  	return &schemaProperty{}
   190  }
   191  
   192  // SetName - sets the name of the property
   193  func (p *schemaProperty) SetName(name string) TypePropertyBuilder {
   194  	p.name = name
   195  	return p
   196  }
   197  
   198  // SetLabel - sets the label of the property
   199  func (p *schemaProperty) SetLabel(label string) TypePropertyBuilder {
   200  	p.label = label
   201  	return p
   202  }
   203  
   204  // SetDescription - set the description of the property
   205  func (p *schemaProperty) SetDescription(description string) TypePropertyBuilder {
   206  	p.description = description
   207  	return p
   208  }
   209  
   210  // SetRequired - set the property as a required field in the schema
   211  func (p *schemaProperty) SetRequired() TypePropertyBuilder {
   212  	p.required = true
   213  	return p
   214  }
   215  
   216  // SetReadOnly - set the property as a read only property
   217  func (p *schemaProperty) SetReadOnly() TypePropertyBuilder {
   218  	p.readOnly = true
   219  	return p
   220  }
   221  
   222  // SetHidden - set the property as a hidden property
   223  func (p *schemaProperty) SetHidden() TypePropertyBuilder {
   224  	p.hidden = true
   225  	return p
   226  }
   227  
   228  // IsString - Set the property to be of type string
   229  func (p *schemaProperty) IsString() StringPropertyBuilder {
   230  	p.dataType = DataTypeString
   231  	return &stringSchemaProperty{
   232  		schemaProperty: p,
   233  	}
   234  }
   235  
   236  // IsNumber - Set the property to be of type number
   237  func (p *schemaProperty) IsNumber() NumberPropertyBuilder {
   238  	p.dataType = DataTypeNumber
   239  	return &numberSchemaProperty{
   240  		schemaProperty: p,
   241  	}
   242  }
   243  
   244  // IsInteger - Set the property to be of type integer
   245  func (p *schemaProperty) IsInteger() IntegerPropertyBuilder {
   246  	p.dataType = DataTypeInteger
   247  	return &integerSchemaProperty{
   248  		numberSchemaProperty{
   249  			schemaProperty: p,
   250  		},
   251  	}
   252  }
   253  
   254  // IsArray - Set the property to be of type array
   255  func (p *schemaProperty) IsArray() ArrayPropertyBuilder {
   256  	p.dataType = DataTypeArray
   257  	return &arraySchemaProperty{
   258  		schemaProperty: p,
   259  	}
   260  }
   261  
   262  // IsObject - Set the property to be of type object
   263  func (p *schemaProperty) IsObject() ObjectPropertyBuilder {
   264  	p.dataType = DataTypeObject
   265  	return &objectSchemaProperty{
   266  		schemaProperty: p,
   267  	}
   268  }
   269  
   270  // Build - create a string propertyDefinition for use in the subscription schema builder
   271  func (p *schemaProperty) Build() (*propertyDefinition, error) {
   272  	if p.err != nil {
   273  		return nil, p.err
   274  	}
   275  	if p.name == "" {
   276  		return nil, fmt.Errorf("cannot add a schema property without a name")
   277  	}
   278  
   279  	if p.dataType == "" {
   280  		return nil, fmt.Errorf("schema property named %s must have a data type", p.name)
   281  	}
   282  
   283  	prop := &propertyDefinition{
   284  		Name:        p.name,
   285  		Title:       p.label,
   286  		Type:        p.dataType,
   287  		Description: p.description,
   288  		ReadOnly:    p.readOnly,
   289  		Required:    p.required,
   290  	}
   291  
   292  	if p.hidden {
   293  		prop.Format = "hidden"
   294  	}
   295  	return prop, nil
   296  }
   297  
   298  /**
   299    string property datatype
   300  */
   301  // stringSchemaProperty - adds specific info needed for a string schema property
   302  type stringSchemaProperty struct {
   303  	schemaProperty *schemaProperty
   304  	isEncrypted    bool
   305  	isCopyable     bool
   306  	sortEnums      bool
   307  	firstEnumValue string
   308  	enums          []string
   309  	widget         string
   310  	defaultValue   string
   311  	dependencies   map[string][]PropertyBuilder
   312  	StringPropertyBuilder
   313  }
   314  
   315  // SetEnumValues - add a list of enum values to the property
   316  func (p *stringSchemaProperty) SetEnumValues(values []string) StringPropertyBuilder {
   317  	dict := make(map[string]bool, 0)
   318  
   319  	// use a temp map to filter out any duplicate values from the input
   320  	for _, value := range values {
   321  		if _, ok := dict[value]; !ok {
   322  			dict[value] = true
   323  			p.enums = append(p.enums, value)
   324  		}
   325  	}
   326  
   327  	return p
   328  }
   329  
   330  // SetSortEnumValues - indicates to sort the enums
   331  func (p *stringSchemaProperty) SetSortEnumValues() StringPropertyBuilder {
   332  	p.sortEnums = true
   333  	return p
   334  }
   335  
   336  // SetFirstEnumValue - Sets a first item for enums. Only needed for sorted enums if you want a specific
   337  // item first in the list
   338  func (p *stringSchemaProperty) SetFirstEnumValue(value string) StringPropertyBuilder {
   339  	p.firstEnumValue = value
   340  	return p
   341  }
   342  
   343  func (p *stringSchemaProperty) enumContains(str string) bool {
   344  	for _, v := range p.enums {
   345  		if v == str {
   346  			return true
   347  		}
   348  	}
   349  	return false
   350  }
   351  
   352  // AddEnumValue - Add another value to the list of allowed values for the property
   353  func (p *stringSchemaProperty) AddEnumValue(value string) StringPropertyBuilder {
   354  	if !p.enumContains(value) {
   355  		p.enums = append(p.enums, value)
   356  	}
   357  	return p
   358  }
   359  
   360  // SetDefaultValue - Define the initial value for the property
   361  func (p *stringSchemaProperty) SetDefaultValue(value string) StringPropertyBuilder {
   362  	p.defaultValue = value
   363  	return p
   364  }
   365  
   366  // SetAsTextArea - set the field to be rendered as a textarea box within the UI
   367  func (p *stringSchemaProperty) SetAsTextArea() StringPropertyBuilder {
   368  	p.widget = "textArea"
   369  	return p
   370  }
   371  
   372  // IsEncrypted - Sets that this field needs to be encrypted at rest
   373  func (p *stringSchemaProperty) IsEncrypted() StringPropertyBuilder {
   374  	p.isEncrypted = true
   375  	return p
   376  }
   377  
   378  // IsCopyable - Sets that this field may be copied via the UI
   379  func (p *stringSchemaProperty) IsCopyable() StringPropertyBuilder {
   380  	p.isCopyable = true
   381  	return p
   382  }
   383  
   384  func (p *stringSchemaProperty) AddDependency(value string, property PropertyBuilder) StringPropertyBuilder {
   385  	if p.dependencies == nil {
   386  		p.dependencies = map[string][]PropertyBuilder{}
   387  	}
   388  	_, ok := p.dependencies[value]
   389  	if !ok {
   390  		p.dependencies[value] = make([]PropertyBuilder, 0)
   391  	}
   392  	p.dependencies[value] = append(p.dependencies[value], property)
   393  	return p
   394  }
   395  
   396  // Build - create a string propertyDefinition for use in the subscription schema builder
   397  func (p *stringSchemaProperty) Build() (def *propertyDefinition, err error) {
   398  	def, err = p.schemaProperty.Build()
   399  	if err != nil {
   400  		return
   401  	}
   402  
   403  	// sort if specified to do so
   404  	if p.sortEnums {
   405  		sort.Strings(p.enums)
   406  	}
   407  
   408  	// append item to start if specified
   409  	if p.firstEnumValue != "" {
   410  		p.enums = append([]string{p.firstEnumValue}, p.enums...)
   411  	}
   412  	def.Enum = p.enums
   413  
   414  	// set default value
   415  	if len(p.defaultValue) > 0 {
   416  		if len(p.enums) > 0 {
   417  			// Check validity for defaultValue
   418  			isDefaultValueValid := false
   419  			for _, x := range p.enums {
   420  				if x == p.defaultValue {
   421  					isDefaultValueValid = true
   422  					break
   423  				}
   424  			}
   425  			if !isDefaultValueValid {
   426  				return nil, fmt.Errorf("default value (%s) must be present in the enum list (%s)", p.defaultValue, p.enums)
   427  			}
   428  		}
   429  		def.DefaultValue = p.defaultValue
   430  	}
   431  
   432  	// set if the property is encrypted at rest
   433  	def.IsEncrypted = p.isEncrypted
   434  
   435  	// set if the property is copyable
   436  	def.IsCopyable = p.isCopyable
   437  
   438  	// set field to be rendered as a textarea box within the UI
   439  	def.Widget = p.widget
   440  
   441  	return def, err
   442  }
   443  
   444  // BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
   445  func (p *stringSchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
   446  	if len(p.dependencies) > 0 {
   447  		deps := &oneOfPropertyDefinitions{
   448  			OneOf: make([]*propertyDefinition, 0),
   449  		}
   450  		noDep := make(map[string]bool)
   451  		for _, enum := range p.enums {
   452  			props, ok := p.dependencies[enum]
   453  			if !ok {
   454  				noDep[enum] = true
   455  				continue
   456  			}
   457  
   458  			depDef, err := p.buildDependenciesDef(enum, props)
   459  			if err != nil {
   460  				return nil, err
   461  			}
   462  			deps.OneOf = append(deps.OneOf, depDef)
   463  		}
   464  		if len(noDep) > 0 {
   465  			for enum := range noDep {
   466  				depDef := &propertyDefinition{
   467  					Properties: make(map[string]propertyDefinition),
   468  				}
   469  				depDef.Properties[p.schemaProperty.name] = propertyDefinition{Enum: []string{enum}}
   470  				deps.OneOf = append(deps.OneOf, depDef)
   471  			}
   472  		}
   473  
   474  		return deps, nil
   475  	}
   476  	return nil, nil
   477  }
   478  
   479  func (p *stringSchemaProperty) buildDependenciesDef(val string, props []PropertyBuilder) (*propertyDefinition, error) {
   480  	depDef := &propertyDefinition{
   481  		Properties:   make(map[string]propertyDefinition),
   482  		Dependencies: make(map[string]*oneOfPropertyDefinitions),
   483  	}
   484  	// value match property
   485  	depDef.Properties[p.schemaProperty.name] = propertyDefinition{Enum: []string{val}}
   486  
   487  	for _, prop := range props {
   488  		dp, err := prop.Build()
   489  		if err != nil {
   490  			return nil, err
   491  		}
   492  
   493  		depDef.Properties[dp.Name] = *dp
   494  		dep, err := prop.BuildDependencies()
   495  		if err != nil {
   496  			return nil, err
   497  		}
   498  
   499  		if dep != nil {
   500  			depDef.Dependencies[dp.Name] = dep
   501  		}
   502  	}
   503  	return depDef, nil
   504  }
   505  
   506  /**
   507    number property datatype builder
   508  */
   509  // numberSchemaProperty - adds specific info needed for a number schema property
   510  type numberSchemaProperty struct {
   511  	schemaProperty *schemaProperty
   512  	minValue       *float64 // We use a pointer to differentiate the "blank value" from a chosen 0 min value
   513  	maxValue       *float64 // We use a pointer to differentiate the "blank value" from a chosen 0 max value
   514  	defaultValue   *float64
   515  	PropertyBuilder
   516  }
   517  
   518  // SetMinValue - set the minimum allowed value
   519  func (p *numberSchemaProperty) SetMinValue(min float64) NumberPropertyBuilder {
   520  	p.minValue = &min
   521  	return p
   522  }
   523  
   524  // SetMaxValue - set the maximum allowed value
   525  func (p *numberSchemaProperty) SetMaxValue(max float64) NumberPropertyBuilder {
   526  	p.maxValue = &max
   527  	return p
   528  }
   529  
   530  // SetDefaultValue - Define the initial value for the property
   531  func (p *numberSchemaProperty) SetDefaultValue(value float64) NumberPropertyBuilder {
   532  	p.defaultValue = &value
   533  	return p
   534  }
   535  
   536  // Build - create the propertyDefinition for use in the subscription schema builder
   537  func (p *numberSchemaProperty) Build() (def *propertyDefinition, err error) {
   538  	def, err = p.schemaProperty.Build()
   539  	if err != nil {
   540  		return
   541  	}
   542  
   543  	if p.minValue != nil && p.maxValue != nil && *p.minValue > *p.maxValue {
   544  		return nil, fmt.Errorf("max value (%f) must be greater than min value (%f)", *p.maxValue, *p.minValue)
   545  	}
   546  
   547  	if p.defaultValue != nil {
   548  		if p.minValue != nil && *p.defaultValue < *p.minValue {
   549  			return nil, fmt.Errorf("default value (%f) must be equal or greater than min value (%f)", *p.defaultValue, *p.minValue)
   550  		}
   551  		if p.maxValue != nil && *p.defaultValue > *p.maxValue {
   552  			return nil, fmt.Errorf("default value (%f) must be equal or lower than max value (%f)", *p.defaultValue, *p.maxValue)
   553  		}
   554  		def.DefaultValue = p.defaultValue
   555  	}
   556  
   557  	def.Minimum = p.minValue
   558  	def.Maximum = p.maxValue
   559  	return def, err
   560  }
   561  
   562  // BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
   563  func (p *numberSchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
   564  	return nil, nil
   565  }
   566  
   567  /**
   568    integer property datatype builder
   569  */
   570  // integerSchemaProperty - adds specific info needed for an integer schema property
   571  type integerSchemaProperty struct {
   572  	numberSchemaProperty
   573  }
   574  
   575  // SetMinValue - set the minimum allowed value
   576  func (p *integerSchemaProperty) SetMinValue(min int64) IntegerPropertyBuilder {
   577  	minimum := float64(min)
   578  	p.minValue = &minimum
   579  	return p
   580  }
   581  
   582  // SetMaxValue - set the maximum allowed value
   583  func (p *integerSchemaProperty) SetMaxValue(max int64) IntegerPropertyBuilder {
   584  	maximum := float64(max)
   585  	p.maxValue = &maximum
   586  	return p
   587  }
   588  
   589  // SetDefaultValue - Define the initial value for the property
   590  func (p *integerSchemaProperty) SetDefaultValue(value int64) IntegerPropertyBuilder {
   591  	defaultValue := float64(value)
   592  	p.defaultValue = &defaultValue
   593  	return p
   594  }
   595  
   596  /**
   597    array property datatype builder
   598  */
   599  // arraySchemaProperty - adds specific info needed for an array schema property
   600  type arraySchemaProperty struct {
   601  	schemaProperty *schemaProperty
   602  	items          []propertyDefinition
   603  	minItems       *uint
   604  	maxItems       *uint
   605  	uniqueItems    bool
   606  	PropertyBuilder
   607  }
   608  
   609  // AddItem - add an item in the array property
   610  func (p *arraySchemaProperty) AddItem(item PropertyBuilder) ArrayPropertyBuilder {
   611  	def, err := item.Build()
   612  	if err == nil {
   613  		p.items = append(p.items, *def)
   614  	} else {
   615  		p.schemaProperty.err = err
   616  	}
   617  	return p
   618  }
   619  
   620  // SetMinItems - set the minimum items in the property array
   621  func (p *arraySchemaProperty) SetMinItems(min uint) ArrayPropertyBuilder {
   622  	p.minItems = &min
   623  	return p
   624  }
   625  
   626  // SetMaxItems - set the maximum items in the property array
   627  func (p *arraySchemaProperty) SetMaxItems(max uint) ArrayPropertyBuilder {
   628  	if max < 1 {
   629  		p.schemaProperty.err = fmt.Errorf("max array items must be greater than 0")
   630  	} else {
   631  		p.maxItems = &max
   632  	}
   633  	return p
   634  }
   635  
   636  // setUniqueItems - automatically adds an uniqueItems property to the array if only one element is present
   637  func (p *arraySchemaProperty) setUniqueItems() {
   638  	if len(p.items) == 1 {
   639  		p.uniqueItems = true
   640  	}
   641  }
   642  
   643  // Build - create the propertyDefinition for use in the subscription schema builder
   644  func (p *arraySchemaProperty) Build() (def *propertyDefinition, err error) {
   645  	def, err = p.schemaProperty.Build()
   646  	if err != nil {
   647  		return
   648  	}
   649  
   650  	var anyOfItems *anyOfPropertyDefinitions
   651  	if p.items != nil {
   652  		anyOfItems = &anyOfPropertyDefinitions{p.items}
   653  		p.setUniqueItems()
   654  	}
   655  
   656  	if p.minItems != nil && p.maxItems != nil && *p.minItems > *p.maxItems {
   657  		return nil, fmt.Errorf("max array items (%d) must be greater than min array items (%d)", p.maxItems, p.minItems)
   658  	}
   659  
   660  	def.Items = anyOfItems
   661  	def.MinItems = p.minItems
   662  	def.MaxItems = p.maxItems
   663  	def.UniqueItems = p.uniqueItems
   664  	return def, err
   665  }
   666  
   667  // BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
   668  func (p *arraySchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
   669  	return nil, nil
   670  }
   671  
   672  /**
   673    object property datatype builder
   674  */
   675  // objectSchemaProperty - adds specific info needed for an object schema property
   676  type objectSchemaProperty struct {
   677  	schemaProperty *schemaProperty
   678  	properties     map[string]propertyDefinition
   679  	PropertyBuilder
   680  }
   681  
   682  // AddProperty - Add a property in the object property
   683  func (p *objectSchemaProperty) AddProperty(property PropertyBuilder) ObjectPropertyBuilder {
   684  	def, err := property.Build()
   685  	if err == nil {
   686  		if p.properties == nil {
   687  			p.properties = make(map[string]propertyDefinition, 0)
   688  		}
   689  		p.properties[def.Name] = *def
   690  	} else {
   691  		p.schemaProperty.err = err
   692  	}
   693  	return p
   694  }
   695  
   696  // Build - create the propertyDefinition for use in the subscription schema builder
   697  func (p *objectSchemaProperty) Build() (def *propertyDefinition, err error) {
   698  	def, err = p.schemaProperty.Build()
   699  	if err != nil {
   700  		return
   701  	}
   702  
   703  	var requiredProperties []string
   704  	if p.properties != nil {
   705  		for _, property := range p.properties {
   706  			if property.Required {
   707  				requiredProperties = append(requiredProperties, property.Name)
   708  			}
   709  		}
   710  	}
   711  	if len(requiredProperties) > 0 {
   712  		sort.Strings(requiredProperties)
   713  	}
   714  
   715  	def.Properties = p.properties
   716  	def.RequiredProperties = requiredProperties
   717  	return def, err
   718  }
   719  
   720  // BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
   721  func (p *objectSchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
   722  	return nil, nil
   723  }