Repository: dikhan/terraform-provider-openapi
Pull request #182 (2019-11-09 21:03:27)

Bugfix 181/parent id required arrays subresources
## Proposed changes

Please add as many details as possible about the change here. Does this Pull Request resolve any open issue? If so, please make sure to link to that issue:
                                                              
Fixes: #181 

## Type of change

What type of change does your code introduce to the provider? Please put an `x` (w/o heading/trailing white spaces) 
in the boxes that apply:

- [x] Bug-fix (change that fixes current functionality)
- [ ] New feature (change that adds new functionality)

## Checklist

Please put an `x` (w/o heading/trailing white spaces) in the boxes that apply:

- [x] I have read and followed the [CONTRIBUTING](https://github.com/dikhan/terraform-provider-api/blob/master/.github/CONTRIBUTING.md) guidelines
- [x] I have made sure code compiles correctly
- [x] I have run 'make test' locally from the terraform_provider_api folder and no errors were found
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] I have added/updated necessary documentation (if appropriate)


Patch:
diff --git a/go.sum b/go.sum
index 48d9fa72..32aa2d36 100644
--- a/go.sum
+++ b/go.sum
@@ -362,6 +362,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
 golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE=
 golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/openapi/openapi_v2_resource.go b/openapi/openapi_v2_resource.go
index 7609e4be..75443f9d 100644
--- a/openapi/openapi_v2_resource.go
+++ b/openapi/openapi_v2_resource.go
@@ -329,31 +329,43 @@ func (o *SpecV2Resource) getParentResourceInfo() *parentResourceInfo {
 }
 
 func (o *SpecV2Resource) getResourceSchema() (*specSchemaDefinition, error) {
-	return o.getSchemaDefinition(&o.SchemaDefinition)
+	return o.getSchemaDefinitionWithOptions(&o.SchemaDefinition, true)
 }
 
 func (o *SpecV2Resource) getSchemaDefinition(schema *spec.Schema) (*specSchemaDefinition, error) {
+	return o.getSchemaDefinitionWithOptions(schema, false)
+}
+
+func (o *SpecV2Resource) getSchemaDefinitionWithOptions(schema *spec.Schema, addParentProps bool) (*specSchemaDefinition, error) {
 	if schema == nil {
 		return nil, fmt.Errorf("schema argument must not be nil")
 	}
 	schemaDefinition := &specSchemaDefinition{}
 	schemaDefinition.Properties = specSchemaDefinitionProperties{}
+
+	// This map ensures no duplicates will happen if the schema happens to have a parent id property. if so, it will be overridden with the expected parent property configuration (e,g: making the prop required)
+	schemaProps := map[string]*specSchemaDefinitionProperty{}
 	for propertyName, property := range schema.Properties {
 		schemaDefinitionProperty, err := o.createSchemaDefinitionProperty(propertyName, property, schema.Required)
 		if err != nil {
 			return nil, err
 		}
-		schemaDefinition.Properties = append(schemaDefinition.Properties, schemaDefinitionProperty)
+		schemaProps[propertyName] = schemaDefinitionProperty
+	}
+	if addParentProps {
+		parentResourceInfo := o.getParentResourceInfo()
+		if parentResourceInfo != nil {
+			parentPropertyNames := parentResourceInfo.getParentPropertiesNames()
+			for _, parentPropertyName := range parentPropertyNames {
+				pr, _ := o.createSchemaDefinitionProperty(parentPropertyName, spec.Schema{SchemaProps: spec.SchemaProps{Type: spec.StringOrArray{"string"}}}, []string{parentPropertyName})
+				pr.IsParentProperty = true
+				schemaProps[parentPropertyName] = pr
+			}
+		}
 	}
 
-	parentResourceInfo := o.getParentResourceInfo()
-	if parentResourceInfo != nil {
-		parentPropertyNames := parentResourceInfo.getParentPropertiesNames()
-		for _, parentPropertyName := range parentPropertyNames {
-			pr, _ := o.createSchemaDefinitionProperty(parentPropertyName, spec.Schema{SchemaProps: spec.SchemaProps{Type: spec.StringOrArray{"string"}}}, []string{parentPropertyName})
-			pr.IsParentProperty = true
-			schemaDefinition.Properties = append(schemaDefinition.Properties, pr)
-		}
+	for _, property := range schemaProps {
+		schemaDefinition.Properties = append(schemaDefinition.Properties, property)
 	}
 	return schemaDefinition, nil
 }


Test patch:
diff --git a/openapi/openapi_v2_resource_test.go b/openapi/openapi_v2_resource_test.go
index 4b13a32f..9b822d83 100644
--- a/openapi/openapi_v2_resource_test.go
+++ b/openapi/openapi_v2_resource_test.go
@@ -1072,7 +1072,7 @@ func assertSchemaParentProperty(actualSpecSchemaDefinition *specSchemaDefinition
 }
 
 func TestGetResourceSchema(t *testing.T) {
-	Convey("Given a SpecV2Resource containing a root path", t, func() {
+	Convey("Given a SpecV2Resource containing a root path and various properties in the schema", t, func() {
 		r := &SpecV2Resource{
 			Path: "/cdns",
 			SchemaDefinition: spec.Schema{
@@ -1111,7 +1111,7 @@ func TestGetResourceSchema(t *testing.T) {
 				},
 			},
 		}
-		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
+		Convey("When getResourceSchema is called", func() {
 			specSchemaDefinition, err := r.getResourceSchema()
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
@@ -1125,56 +1125,47 @@ func TestGetResourceSchema(t *testing.T) {
 			})
 		})
 	})
-}
 
-func TestGetSchemaDefinition(t *testing.T) {
-
-	Convey("Given a blank SpecV2Resource", t, func() {
-		r := &SpecV2Resource{}
-		Convey("When getSchemaDefinition is called with a nil arg", func() {
-			_, err := r.getSchemaDefinition(nil)
-			Convey("Then the error returned matches the expected one", func() {
-				So(err.Error(), ShouldEqual, "schema argument must not be nil")
-			})
-		})
-		Convey("When getSchemaDefinition is called passing a blank schema", func() {
-			d, e := r.getSchemaDefinition(&spec.Schema{})
-			Convey("Then the error returned is not nil", func() {
-				So(e, ShouldBeNil)
-			})
-			Convey("And the schema definition contains empty Properties", func() {
-				So(d, ShouldNotBeNil)
-				So(d.Properties, ShouldBeEmpty)
-			})
-		})
-		Convey("When getSchemaDefinition is called passing a schema with a weird property type", func() {
-			schema := spec.Schema{
+	Convey("Given a SpecV2Resource containing a sub-resource path (one level) that has a weird sub-resource path (firewalls is missing firewalls/{id}) and a schema definition with a property", t, func() {
+		r := &SpecV2Resource{
+			Path: "/v1/cdns/{id}/firewalls/v1/rules",
+			SchemaDefinition: spec.Schema{
 				SchemaProps: spec.SchemaProps{
+					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
 						"string_readonly_prop": {
 							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"something weird"},
+								Type: spec.StringOrArray{"string"},
+							},
+							SwaggerSchemaProps: spec.SwaggerSchemaProps{
+								ReadOnly: true,
 							},
 						},
 					},
 				},
-			}
-			d, e := r.getSchemaDefinition(&schema)
-			Convey("Then the error returned is not nil", func() {
-				So(e, ShouldNotBeNil)
+			},
+		}
+		Convey("When getResourceSchema is called", func() {
+			specSchemaDefinition, err := r.getResourceSchema()
+			Convey("Then the error returned should be nil", func() {
+				So(err, ShouldBeNil)
 			})
-			Convey("And the schema definition returned is nil", func() {
-				So(d, ShouldBeNil)
+			Convey("And the specSchemaDefinition returned should be configured with the expected number of properties including the parent id one", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 2)
+			})
+			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
+				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
+			})
+			Convey("And the specSchemaDefinition returned should be configured with the parent id property marked as IsParentProperty, with the right name, type and being required", func() {
+				assertSchemaParentProperty(specSchemaDefinition, "cdns_v1_id")
 			})
 		})
-
 	})
-	Convey("Given a SpecV2Resource containing a root path", t, func() {
+
+	Convey("Given a SpecV2Resource containing a sub-resource path (one level and parent versioned) and a schema containing one property", t, func() {
 		r := &SpecV2Resource{
-			Path: "/cdns",
-		}
-		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
-			s := &spec.Schema{
+			Path: "/v1/cdns/{id}/firewalls",
+			SchemaDefinition: spec.Schema{
 				SchemaProps: spec.SchemaProps{
 					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
@@ -1186,51 +1177,32 @@ func TestGetSchemaDefinition(t *testing.T) {
 								ReadOnly: true,
 							},
 						},
-						"int_optional_computed_prop": {
-							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"integer"},
-							},
-							VendorExtensible: spec.VendorExtensible{
-								Extensions: spec.Extensions{
-									extTfComputed: true,
-								},
-							},
-						},
-						"number_required_prop": {
-							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"number"},
-							},
-						},
-						"bool_prop": {
-							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"boolean"},
-							},
-						},
 					},
 				},
-			}
-			specSchemaDefinition, err := r.getSchemaDefinition(s)
+			},
+		}
+		Convey("When getResourceSchema is called", func() {
+			specSchemaDefinition, err := r.getResourceSchema()
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
+			Convey("And the specSchemaDefinition returned should be configured with the expected number of properties including the parent id one", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 2)
+			})
 			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, len(s.SchemaProps.Properties))
 				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
-				assertSchemaProperty(specSchemaDefinition, "int_optional_computed_prop", typeInt, false, false, true)
-				assertSchemaProperty(specSchemaDefinition, "number_required_prop", typeFloat, true, false, false)
-				assertSchemaProperty(specSchemaDefinition, "bool_prop", typeBool, false, false, false)
+			})
+			Convey("And the specSchemaDefinition returned should be configured with the parent id property with the expected configuration", func() {
+				assertSchemaParentProperty(specSchemaDefinition, "cdns_v1_id")
 			})
 		})
 	})
 
-	Convey("Given a SpecV2Resource containing a subresource path (one level)", t, func() {
+	Convey("Given a SpecV2Resource that is a sub-resource (one level parent with no version)", t, func() {
 		r := &SpecV2Resource{
-			Path: "/v1/cdns/{id}/firewalls",
-		}
-		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
-			s := &spec.Schema{
+			Path: "/parent/{parent_id}/child",
+			SchemaDefinition: spec.Schema{
 				SchemaProps: spec.SchemaProps{
-					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
 						"string_readonly_prop": {
 							SchemaProps: spec.SchemaProps{
@@ -1240,54 +1212,80 @@ func TestGetSchemaDefinition(t *testing.T) {
 								ReadOnly: true,
 							},
 						},
-						"int_optional_computed_prop": {
-							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"integer"},
-							},
-							VendorExtensible: spec.VendorExtensible{
-								Extensions: spec.Extensions{
-									extTfComputed: true,
-								},
-							},
-						},
-						"number_required_prop": {
+					},
+				},
+			},
+		}
+		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
+			specSchemaDefinition, err := r.getResourceSchema()
+			Convey("Then the error returned should be nil", func() {
+				So(err, ShouldBeNil)
+			})
+			Convey("And the specSchemaDefinition returned should contain the expected number of properties (including the parent one)", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 2)
+			})
+			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
+				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
+			})
+			Convey("And the specSchemaDefinition returned should also include the parent property", func() {
+				assertSchemaParentProperty(specSchemaDefinition, "parent_id")
+			})
+		})
+	})
+
+	Convey("Given a SpecV2Resource that is a sub-resource (two level parent) and a schema for child", t, func() {
+		r := &SpecV2Resource{
+			Path: "/parent/{parent_id}/subparent/{subparent_id}/child",
+			SchemaDefinition: spec.Schema{
+				SchemaProps: spec.SchemaProps{
+					Properties: map[string]spec.Schema{
+						"string_readonly_prop": {
 							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"number"},
+								Type: spec.StringOrArray{"string"},
 							},
-						},
-						"bool_prop": {
-							SchemaProps: spec.SchemaProps{
-								Type: spec.StringOrArray{"boolean"},
+							SwaggerSchemaProps: spec.SwaggerSchemaProps{
+								ReadOnly: true,
 							},
 						},
 					},
 				},
-			}
-			specSchemaDefinition, err := r.getSchemaDefinition(s)
+			},
+		}
+		Convey("When getResourceSchema is called", func() {
+			specSchemaDefinition, err := r.getResourceSchema()
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
-			Convey("And the specSchemaDefinition returned should be configured with the expected number of properties including the parent id one", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, len(s.SchemaProps.Properties)+1)
+			Convey("And the specSchemaDefinition returned should contain the expected number of properties (including the parent ones)", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 3)
 			})
 			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
 				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
-				assertSchemaProperty(specSchemaDefinition, "int_optional_computed_prop", typeInt, false, false, true)
-				assertSchemaProperty(specSchemaDefinition, "number_required_prop", typeFloat, true, false, false)
-				assertSchemaProperty(specSchemaDefinition, "bool_prop", typeBool, false, false, false)
 			})
-			Convey("And the specSchemaDefinition returned should be configured with the parent id property with the expected configuration", func() {
-				assertSchemaParentProperty(specSchemaDefinition, "cdns_v1_id")
+			Convey("And the specSchemaDefinition returned should also include the parents properties", func() {
+				assertSchemaParentProperty(specSchemaDefinition, "parent_id")
+				assertSchemaParentProperty(specSchemaDefinition, "subparent_id")
 			})
 		})
 	})
 
-	Convey("Given a SpecV2Resource containing a subresource path (one level) that has a non restful subresource path", t, func() {
-		r := &SpecV2Resource{
-			Path: "/v1/cdns/{id}/firewalls/v1/rules",
-		}
-		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
-			s := &spec.Schema{
+	Convey("Given a SpecV2Resource containing a sub-resource path (one level) and the parent resource using a preferred resource name", t, func() {
+		r := SpecV2Resource{
+			Path: "/v1/cdns/{id}/firewalls",
+			Paths: map[string]spec.PathItem{
+				"/v1/cdns": {
+					PathItemProps: spec.PathItemProps{
+						Post: &spec.Operation{
+							VendorExtensible: spec.VendorExtensible{
+								Extensions: spec.Extensions{
+									extTfResourceName: "cdn",
+								},
+							},
+						},
+					},
+				},
+			},
+			SchemaDefinition: spec.Schema{
 				SchemaProps: spec.SchemaProps{
 					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
@@ -1301,30 +1299,30 @@ func TestGetSchemaDefinition(t *testing.T) {
 						},
 					},
 				},
-			}
-			specSchemaDefinition, err := r.getSchemaDefinition(s)
+			},
+		}
+		Convey("When getResourceSchema is called", func() {
+			specSchemaDefinition, err := r.getResourceSchema()
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
 			Convey("And the specSchemaDefinition returned should be configured with the expected number of properties including the parent id one", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, len(s.SchemaProps.Properties)+1)
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 2)
 			})
 			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
 				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
 			})
-			Convey("And the specSchemaDefinition returned should be configured with the parent id property marked as IsParentProperty, with the right name, type and being required", func() {
-				assertSchemaParentProperty(specSchemaDefinition, "cdns_v1_id")
+			Convey("And the specSchemaDefinition returned should be configured with the parent id property named using the preferred parent name configured in the parent resource", func() {
+				assertSchemaParentProperty(specSchemaDefinition, "cdn_v1_id")
 			})
 		})
 	})
 
-	Convey("Given a SpecV2Resource containing a subresource path (two level)", t, func() {
+	Convey("Given a SpecV2Resource containing a subresource path (two level and parents versioned)", t, func() {
 		r := &SpecV2Resource{
 			Path:  "/v1/cdns/{cdn_id}/v2/firewalls/{fw_id}/rules",
 			Paths: nil,
-		}
-		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
-			s := &spec.Schema{
+			SchemaDefinition: spec.Schema{
 				SchemaProps: spec.SchemaProps{
 					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
@@ -1358,13 +1356,15 @@ func TestGetSchemaDefinition(t *testing.T) {
 						},
 					},
 				},
-			}
-			specSchemaDefinition, err := r.getSchemaDefinition(s)
+			},
+		}
+		Convey("When getResourceSchema is called", func() {
+			specSchemaDefinition, err := r.getResourceSchema()
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
 			Convey("And the specSchemaDefinition returned should be configured with the expected number of properties including the parent id ones", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, len(s.SchemaProps.Properties)+2)
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 6)
 			})
 			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
 				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
@@ -1378,14 +1378,18 @@ func TestGetSchemaDefinition(t *testing.T) {
 			})
 		})
 	})
+}
+
+func TestGetSchemaDefinition(t *testing.T) {
 
-	Convey("Given a SpecV2Resource that is a subresource (one level parent)", t, func() {
+	Convey("Given a SpecV2Resource containing a root path", t, func() {
 		r := &SpecV2Resource{
-			Path: "/parent/{parent_id}/child",
+			Path: "/cdns",
 		}
 		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
 			s := &spec.Schema{
 				SchemaProps: spec.SchemaProps{
+					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
 						"string_readonly_prop": {
 							SchemaProps: spec.SchemaProps{
@@ -1395,6 +1399,26 @@ func TestGetSchemaDefinition(t *testing.T) {
 								ReadOnly: true,
 							},
 						},
+						"int_optional_computed_prop": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"integer"},
+							},
+							VendorExtensible: spec.VendorExtensible{
+								Extensions: spec.Extensions{
+									extTfComputed: true,
+								},
+							},
+						},
+						"number_required_prop": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"number"},
+							},
+						},
+						"bool_prop": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"boolean"},
+							},
+						},
 					},
 				},
 			}
@@ -1402,27 +1426,25 @@ func TestGetSchemaDefinition(t *testing.T) {
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
-			Convey("And the specSchemaDefinition returned should contain the expected number of properties (including the parent one)", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, 2)
-			})
 			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, len(s.SchemaProps.Properties))
 				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
-			})
-			Convey("And the specSchemaDefinition returned should also include the parent property", func() {
-				assertSchemaParentProperty(specSchemaDefinition, "parent_id")
+				assertSchemaProperty(specSchemaDefinition, "int_optional_computed_prop", typeInt, false, false, true)
+				assertSchemaProperty(specSchemaDefinition, "number_required_prop", typeFloat, true, false, false)
+				assertSchemaProperty(specSchemaDefinition, "bool_prop", typeBool, false, false, false)
 			})
 		})
 	})
 
-	Convey("Given a SpecV2Resource that is a subresource (two level parent)", t, func() {
+	Convey("Given a SpecV2Resource containing a sub-resource", t, func() {
 		r := &SpecV2Resource{
-			Path: "/parent/{parent_id}/subparent/{subparent_id}/child",
+			Path: "/zone/{zone_id}/recordset",
 		}
 		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
 			s := &spec.Schema{
 				SchemaProps: spec.SchemaProps{
 					Properties: map[string]spec.Schema{
-						"string_readonly_prop": {
+						"id": {
 							SchemaProps: spec.SchemaProps{
 								Type: spec.StringOrArray{"string"},
 							},
@@ -1437,42 +1459,106 @@ func TestGetSchemaDefinition(t *testing.T) {
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
-			Convey("And the specSchemaDefinition returned should contain the expected number of properties (including the parent ones)", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, 3)
+			Convey("And the specSchemaDefinition returned should be 1 and no parent id should be in the schema. Only getResourceSchema() allows parent ids to be injects", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 1)
+				assertSchemaProperty(specSchemaDefinition, "id", typeString, false, true, true)
 			})
-			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
-				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
+		})
+	})
+}
+
+func TestGetSchemaDefinitionWithOptions(t *testing.T) {
+
+	Convey("Given a blank SpecV2Resource", t, func() {
+		r := &SpecV2Resource{}
+		Convey("When getSchemaDefinitionWithOptions is called with a nil arg", func() {
+			_, err := r.getSchemaDefinitionWithOptions(nil, true)
+			Convey("Then the error returned matches the expected one", func() {
+				So(err.Error(), ShouldEqual, "schema argument must not be nil")
 			})
-			Convey("And the specSchemaDefinition returned should also include the parents properties", func() {
-				assertSchemaParentProperty(specSchemaDefinition, "parent_id")
-				assertSchemaParentProperty(specSchemaDefinition, "subparent_id")
+		})
+		Convey("When getSchemaDefinitionWithOptions is called passing a blank schema", func() {
+			d, e := r.getSchemaDefinitionWithOptions(&spec.Schema{}, true)
+			Convey("Then the error returned is not nil", func() {
+				So(e, ShouldBeNil)
+			})
+			Convey("And the schema definition contains empty Properties", func() {
+				So(d, ShouldNotBeNil)
+				So(d.Properties, ShouldBeEmpty)
+			})
+		})
+		Convey("When getSchemaDefinitionWithOptions is called passing a schema with a weird property type", func() {
+			schema := spec.Schema{
+				SchemaProps: spec.SchemaProps{
+					Properties: map[string]spec.Schema{
+						"string_readonly_prop": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"something weird"},
+							},
+						},
+					},
+				},
+			}
+			d, e := r.getSchemaDefinitionWithOptions(&schema, true)
+			Convey("Then the error returned is not nil", func() {
+				So(e, ShouldNotBeNil)
+			})
+			Convey("And the schema definition returned is nil", func() {
+				So(d, ShouldBeNil)
 			})
 		})
 	})
 
-	Convey("Given a SpecV2Resource containing a sub-resource path (one level) and the parent resource using a preferred resource name", t, func() {
-		r := SpecV2Resource{
-			Path: "/v1/cdns/{id}/firewalls",
-			Paths: map[string]spec.PathItem{
-				"/v1/cdns": {
-					PathItemProps: spec.PathItemProps{
-						Post: &spec.Operation{
-							VendorExtensible: spec.VendorExtensible{
-								Extensions: spec.Extensions{
-									extTfResourceName: "cdn",
-								},
+	Convey("Given a SpecV2Resource containing a sub-resource path (one level) with a schema containing a property that matches the parent property id", t, func() {
+		r := &SpecV2Resource{
+			Path: "/zone/{zone_id}/recordset",
+			SchemaDefinition: spec.Schema{
+				SchemaProps: spec.SchemaProps{
+					Required: []string{"number_required_prop"},
+					Properties: map[string]spec.Schema{
+						"id": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"string"},
+							},
+							SwaggerSchemaProps: spec.SwaggerSchemaProps{
+								ReadOnly: true,
+							},
+						},
+						"zone_id": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"string"},
 							},
 						},
 					},
 				},
 			},
 		}
-		Convey("When getSchemaDefinition is called with a schema containing various properties", func() {
-			s := &spec.Schema{
+		Convey("When getSchemaDefinitionWithOptions is called with the addParentProps set to true", func() {
+			specSchemaDefinition, err := r.getSchemaDefinitionWithOptions(&r.SchemaDefinition, true)
+			Convey("Then the error returned should be nil", func() {
+				So(err, ShouldBeNil)
+			})
+			Convey("And the specSchemaDefinition returned should be configured with the id and the zone_id properties (and no extra parent property 'zone_id' will be added since it's already there)", func() {
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 2)
+			})
+			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
+				assertSchemaProperty(specSchemaDefinition, "id", typeString, false, true, true)
+			})
+			Convey("And the specSchemaDefinition returned should be configured with the parent id property with the expected configuration", func() {
+				// Note due to the model already containing a parent id property (zone_id) it will be reconfigured to be required. This ensures the resource tf configuration requires the parent id property to be populated.
+				assertSchemaParentProperty(specSchemaDefinition, "zone_id")
+			})
+		})
+	})
+
+	Convey("Given a SpecV2Resource containing a sub-resource path with a schema containing an array of objects", t, func() {
+		r := &SpecV2Resource{
+			Path: "/zone/{zone_id}/recordset",
+			SchemaDefinition: spec.Schema{
 				SchemaProps: spec.SchemaProps{
 					Required: []string{"number_required_prop"},
 					Properties: map[string]spec.Schema{
-						"string_readonly_prop": {
+						"id": {
 							SchemaProps: spec.SchemaProps{
 								Type: spec.StringOrArray{"string"},
 							},
@@ -1480,21 +1566,48 @@ func TestGetSchemaDefinition(t *testing.T) {
 								ReadOnly: true,
 							},
 						},
+						"record": {
+							SchemaProps: spec.SchemaProps{
+								Type: spec.StringOrArray{"array"},
+								Items: &spec.SchemaOrArray{
+									Schema: &spec.Schema{
+										SchemaProps: spec.SchemaProps{
+											Type: spec.StringOrArray{"object"},
+											Properties: map[string]spec.Schema{
+												"content": {
+													SchemaProps: spec.SchemaProps{
+														Type: spec.StringOrArray{"string"},
+													},
+												},
+											},
+										},
+									},
+								},
+							},
+						},
 					},
 				},
-			}
-			specSchemaDefinition, err := r.getSchemaDefinition(s)
+			},
+		}
+		Convey("When getSchemaDefinitionWithOptions is called with addParentProps set to true", func() {
+			specSchemaDefinition, err := r.getSchemaDefinitionWithOptions(&r.SchemaDefinition, true)
 			Convey("Then the error returned should be nil", func() {
 				So(err, ShouldBeNil)
 			})
 			Convey("And the specSchemaDefinition returned should be configured with the expected number of properties including the parent id one", func() {
-				So(len(specSchemaDefinition.Properties), ShouldEqual, len(s.SchemaProps.Properties)+1)
+				So(len(specSchemaDefinition.Properties), ShouldEqual, 3)
 			})
 			Convey("And the specSchemaDefinition returned should be configured as expected", func() {
-				assertSchemaProperty(specSchemaDefinition, "string_readonly_prop", typeString, false, true, true)
+				assertSchemaProperty(specSchemaDefinition, "id", typeString, false, true, true)
+				assertSchemaProperty(specSchemaDefinition, "record", typeList, false, false, false)
 			})
-			Convey("And the specSchemaDefinition returned should be configured with the parent id property named using the preferred parent name configured in the parent resource", func() {
-				assertSchemaParentProperty(specSchemaDefinition, "cdn_v1_id")
+			Convey("And the specSchemaDefinition for the array property should not contain any parent id", func() {
+				recordProp, _ := specSchemaDefinition.getProperty("record")
+				So(len(recordProp.SpecSchemaDefinition.Properties), ShouldEqual, 1)
+				So(recordProp.SpecSchemaDefinition.Properties[0].Name, ShouldEqual, "content")
+			})
+			Convey("And the specSchemaDefinition returned should be configured with the parent id property with the expected configuration", func() {
+				assertSchemaParentProperty(specSchemaDefinition, "zone_id")
 			})
 		})
 	})