Skip to content

Commit

Permalink
add implementation for getSuccessfulResponseDefinition
Browse files Browse the repository at this point in the history
- added unit tests
  • Loading branch information
dikhan committed Oct 29, 2019
1 parent 11d03ee commit dfdc4b2
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 15 deletions.
34 changes: 19 additions & 15 deletions openapi/openapi_v2_spec_analyser.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,12 @@ func (specAnalyser *specV2Analyser) validateRootPath(resourcePath string) (strin
if err != nil {
bodyParam := specAnalyser.bodyParameterExists(resourceRootPostOperation)
if bodyParam == nil {
resourceSchema, _ := specAnalyser.getResponseModel(resourceRootPostOperation) // TODO: implement this
resourceSchema, _ := specAnalyser.getSuccessfulResponseDefinition(resourceRootPostOperation) // TODO: implement this
err := specAnalyser.validateResourceSchemaDefWithOptions(resourceSchema, true)
if err != nil {
return "", nil, nil, fmt.Errorf("resource root path '%s' POST operation validation error: %s", resourceRootPath, err)
}
// TODO: if we have reached this poiunt, that means that the root path should also be considered valid even though the post does not contain a body param. Hence, the resourceRootPostSchemaDef should instead be assigned to the model returned by getResponseModel
// TODO: if we have reached this poiunt, that means that the root path should also be considered valid even though the post does not contain a body param. Hence, the resourceRootPostSchemaDef should instead be assigned to the model returned by getSuccessfulResponseDefinition
// TODO: assign resourceRootPostSchemaDef = resourceSchema
// TODO: return resourceRootPath, &resourceRootPathItem, resourceRootPostSchemaDef, nil
}
Expand All @@ -360,19 +360,23 @@ func (specAnalyser *specV2Analyser) validateRootPath(resourcePath string) (strin
return resourceRootPath, &resourceRootPathItem, resourceRootPostSchemaDef, nil
}

// getResponseModel is responsible for getting the model definition from the response
func (specAnalyser *specV2Analyser) getResponseModel(operation *spec.Operation) (*spec.Schema, error) {
// TODO: For instance, for the following endpoint post operation the method should return the schema associated with 201 (already expanded - operation.Responses.ResponsesProps.StatusCodeResponses[201].Schema).
// Note the operation could be async or it could return 200. In the case where there are multiple successful responses like 200, 201 and 202 the method should return
// and error. Otherwise, it should return the corresponding schema associated with the 'succesful' response code (whathever exists, either 200 or 201 or 202)
// /v1/deployKey:
// post:
// x-terraform-resource-name: "deploykey"
// responses:
// 201:
// schema:
// $ref: "#/definitions/DeployKeyV1"
return nil, nil
// getSuccessfulResponseDefinition is responsible for getting the model definition from the response that matches a successful
// response (either 200, 201 or 202 whichever is found first). It is assumed that the the responses will only include one of the
// aforementioned successful responses, if multiple are present the first one found will be selected and its corresponding schema
// will be returned
func (specAnalyser *specV2Analyser) getSuccessfulResponseDefinition(operation *spec.Operation) (*spec.Schema, error) {
if operation == nil || operation.Responses == nil {
return nil, fmt.Errorf("operation is missing responses")
}
for responseStatusCode, response := range operation.Responses.ResponsesProps.StatusCodeResponses {
if responseStatusCode == http.StatusOK || responseStatusCode == http.StatusCreated || responseStatusCode == http.StatusAccepted {
if response.Schema == nil {
return nil, fmt.Errorf("operation response '%d' is missing the schema definitnion", responseStatusCode)
}
return response.Schema, nil
}
}
return nil, fmt.Errorf("operation is missing successful response")
}

func (specAnalyser *specV2Analyser) validateResourceSchemaDefWithOptions(schema *spec.Schema, shouldPropBeReadOnly bool) error {
Expand Down
207 changes: 207 additions & 0 deletions openapi/openapi_v2_spec_analyser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,213 @@ func Test_bodyParameterExists(t *testing.T) {
})
}

func Test_getSuccessfulResponseDefinition(t *testing.T) {
testCases := []struct {
name string
inputOperation *spec.Operation
expectedSchema *spec.Schema
expectedError error
}{
{
// Example:
// post:
// responses:
// 201:
// schema:
// ...
name: "operation contains a valid successful response (200 OK) schema among other non relevant (for this test) responses",
inputOperation: &spec.Operation{
OperationProps: spec.OperationProps{
Responses: &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: spec.Response{
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"id": spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
ReadOnly: true,
},
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{"string"},
},
},
},
},
},
},
},
http.StatusNotFound: spec.Response{},
},
},
},
},
},
expectedSchema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"id": spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
ReadOnly: true,
},
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{"string"},
},
},
},
},
},
expectedError: nil,
},
{
name: "operation contains a valid successful response (201 Created) schema",
inputOperation: &spec.Operation{
OperationProps: spec.OperationProps{
Responses: &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusCreated: spec.Response{
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"id": spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
ReadOnly: true,
},
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{"string"},
},
},
},
},
},
},
},
},
},
},
},
},
expectedSchema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"id": spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
ReadOnly: true,
},
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{"string"},
},
},
},
},
},
expectedError: nil,
},
{
name: "operation contains a valid successful response (202 Accepted) schema",
inputOperation: &spec.Operation{
OperationProps: spec.OperationProps{
Responses: &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusAccepted: spec.Response{
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"id": spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
ReadOnly: true,
},
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{"string"},
},
},
},
},
},
},
},
},
},
},
},
},
expectedSchema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"id": spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
ReadOnly: true,
},
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{"string"},
},
},
},
},
},
expectedError: nil,
},
{
name: "operation contains a valid successful response (200) but the schema is missing",
inputOperation: &spec.Operation{
OperationProps: spec.OperationProps{
Responses: &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: spec.Response{
ResponseProps: spec.ResponseProps{
Schema: nil,
},
},
},
},
},
},
},
expectedSchema: nil,
expectedError: errors.New("operation response '200' is missing the schema definitnion"),
},
{
name: "operation does not contain a valid successful response (200, 201 or 202) schema",
inputOperation: &spec.Operation{
OperationProps: spec.OperationProps{
Responses: &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusNotFound: spec.Response{},
},
},
},
},
},
expectedSchema: nil,
expectedError: errors.New("operation is missing successful response"),
},
{
name: "operation is nil",
inputOperation: nil,
expectedSchema: nil,
expectedError: errors.New("operation is missing responses"),
},
}

for _, tc := range testCases {
specV2Analyser := specV2Analyser{}
s, err := specV2Analyser.getSuccessfulResponseDefinition(tc.inputOperation)
if tc.expectedError != nil {
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
} else {
assert.Equal(t, tc.expectedSchema, s, tc.name)
}
}
}

func Test_getBodyParameterBodySchema(t *testing.T) {
Convey("Given a specV2Analyser", t, func() {
specV2Analyser := &specV2Analyser{}
Expand Down

0 comments on commit dfdc4b2

Please sign in to comment.