Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for shared responses defs #87

Merged
merged 1 commit into from
Jul 17, 2019

Conversation

Kosta-Github
Copy link

E.g. sample input schema:

{
    "swagger": "2.0",
    "info": {
        "version": "0.0.1",
        "title": "your title"
    },
    "paths": {
        "/persons": {
            "get": {
                "operationId": "get_person",
                "description": "Gets `Person` object.",
                "responses": {
                    "200": { "description": "empty schema" },
                    "201": {
                        "description": "inline schema",
                        "schema": { "type": "object", "properties": { "inline_schema": { "type": "string" } } }
                    },
                    "202": {
                        "description": "inline schema ref",
                        "schema": { "$ref": "#/definitions/direct_schema_ref" }
                    },
                    "203": { "$ref": "#/responses/empty_schema" },
                    "204": { "$ref": "#/responses/regular_schema" },
                    "205": { "$ref": "#/responses/indirect_schema" },
                    "206": { "$ref": "#/responses/same_name" },
                    "207": { "$ref": "#/responses/same_name_clash" }
                }
            }
        }
    },
    "responses": {
        "empty_schema": { "description": "empty schema" },
        "regular_schema": {
            "description": "regular schema",
            "schema": { "type": "object", "properties": { "regular_schema": { "type": "string" } } }
        },
        "indirect_schema": {
            "description": "indirect schema",
            "schema": { "$ref": "#/definitions/some_def" }
        },
        "same_name": {
            "description": "same name schema",
            "schema": { "$ref": "#/definitions/same_name" }
        },
        "same_name_clash": {
            "description": "same name clash schema",
            "schema": { "type": "object", "properties": { "same_name_clash_response": { "type": "string" } } }
        }
    },
    "definitions": {
        "direct_schema_ref": { "type": "object", "properties": { "direct_schema_ref": { "type": "string" } } },
        "some_def": { "type": "object", "properties": { "some_def": { "type": "string" } } },
        "same_name": { "type": "object", "properties": { "same_name": { "type": "string" } } },
        "same_name_clash": { "type": "object", "properties": { "same_name_clash_definition": { "type": "string" } } }
    }
}

gets normalized to:

{
  "swagger": "2.0",
  "info": {
    "version": "0.0.1",
    "title": "your title"
  },
  "paths": {
    "/persons": {
      "get": {
        "operationId": "get_person",
        "description": "Gets `Person` object.",
        "responses": {
          "200": { "description": "empty schema" },
          "201": {
            "description": "inline schema",
            "schema": { "$ref": "#/definitions/Response_get_person_201" }
          },
          "202": {
            "description": "inline schema ref",
            "schema": { "$ref": "#/definitions/direct_schema_ref" }
          },
          "203": { "description": "empty schema" },
          "204": {
            "description": "regular schema",
            "schema": { "$ref": "#/definitions/regular_schema" }
          },
          "205": {
            "description": "indirect schema",
            "schema": { "$ref": "#/definitions/some_def" }
          },
          "206": {
            "description": "same name schema",
            "schema": { "$ref": "#/definitions/same_name" }
          },
          "207": {
            "description": "same name clash schema",
            "schema": { "$ref": "#/definitions/Response_same_name_clash" }
          }
        }
      }
    }
  },
  "definitions": {
    "direct_schema_ref": {
      "type": "object",
      "properties": {
        "direct_schema_ref": { "type": "string" }
      }
    },
    "some_def": {
      "type": "object",
      "properties": {
        "some_def": { "type": "string" }
      }
    },
    "same_name": {
      "type": "object",
      "properties": {
        "same_name": { "type": "string" }
      }
    },
    "same_name_clash": {
      "type": "object",
      "properties": {
        "same_name_clash_definition": { "type": "string" }
      }
    },
    "regular_schema": {
      "type": "object",
      "properties": {
        "regular_schema": { "type": "string" }
      }
    },
    "Response_same_name_clash": {
      "type": "object",
      "properties": {
        "same_name_clash_response": { "type": "string" }
      }
    },
    "Response_get_person_201": {
      "type": "object",
      "properties": {
        "inline_schema": { "type": "string" }
      }
    }
  }
}
  • inline response types will be removed and be replaced an explicit response type definition (either using an existing response type name) or generating it for the corresponding endpoint, method, and response code
  • this will also improve the response type definitions from something like ResponseWithBody < 200, object > to a more meaningful declaration like: ResponseWithBody < 200, Response_findById_200 >

* @method
* @name ResponsesApi#get_person
*/
get_person(parameters: {} & CommonRequestOptions): Promise < ResponseWithBody < 200, void > | ResponseWithBody < 201, Response_get_person_201 > | ResponseWithBody < 202, direct_schema_ref > | ResponseWithBody < 203, void > | ResponseWithBody < 204, regular_schema > | ResponseWithBody < 205, some_def > | ResponseWithBody < 206, same_name > | ResponseWithBody < 207, Response_same_name_clash >> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResponseWithBody always provides the correct type now; no more untyped object declarations

"204": { "$ref": "#/responses/regular_schema" },
"205": { "$ref": "#/responses/indirect_schema" },
"206": { "$ref": "#/responses/same_name" },
"207": { "$ref": "#/responses/same_name_clash" }
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response codes 200 to 207 should enumerate all possible combinations with respect to response schema definitions

Copy link
Collaborator

@Markionium Markionium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing the work and apologies for the somewhat slow response. @mtennoe is on holiday and it's been a pretty last couple days for me.

@@ -76,6 +77,65 @@ export function getViewForSwagger2(opts: CodeGenOptions): ViewData {
};
}

function normalizeResponseDefinitions(swagger: any): any {
// ensure that the optional swagger.responses and swagger.definitions fields are present
swagger.responses = swagger.responses || {};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nicer to use a local variable here? We're creating this optional empty object on the swagger object and then later delete it.

In any case we're mutating the parameter which isn't really necessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok to merge this as is for now.

@@ -76,6 +77,65 @@ export function getViewForSwagger2(opts: CodeGenOptions): ViewData {
};
}

function normalizeResponseDefinitions(swagger: any): any {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably use CodeGenOptions['swagger'] here and for the return type.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kosta-Github I think once we adjust this type and resolve the conflict we can merge this. Since you mentioned you do not have so much time now, if you could resolve the conflict I can fix this up separately.

([path, httpVerb, op]) => {
const responses = op.responses;

Object.entries<any>(responses).forEach(([resCode, resDef]) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably has a better type once we use the correct type for the function parameter?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we won't need here once we have the type in the comment above fixed.

@Kosta-Github
Copy link
Author

@Markionium based on your comments, I am not sure if I should change something in this PR to get it accepted...

* `swagger` supports the definition of shared `responses` objects which can be ref'ed by several endpoints
* see: https://github.com/reverb/swagger-spec/blob/4678d9fbe5723cb926b50fe0d1ae877c34e44a42/schemas/v2.0/schema.json#L81
* in order to deal with this potential additional level of indirection the swagger schema is normalized with respect to the `responses` defs before the existing algorithm is triggered

E.g. sample input schema:
```json
{
    "swagger": "2.0",
    "info": {
        "version": "0.0.1",
        "title": "your title"
    },
    "paths": {
        "/persons": {
            "get": {
                "operationId": "get_person",
                "description": "Gets `Person` object.",
                "responses": {
                    "200": { "description": "empty schema" },
                    "201": {
                        "description": "inline schema",
                        "schema": { "type": "object", "properties": { "inline_schema": { "type": "string" } } }
                    },
                    "202": {
                        "description": "inline schema ref",
                        "schema": { "$ref": "#/definitions/direct_schema_ref" }
                    },
                    "203": { "$ref": "#/responses/empty_schema" },
                    "204": { "$ref": "#/responses/regular_schema" },
                    "205": { "$ref": "#/responses/indirect_schema" },
                    "206": { "$ref": "#/responses/same_name" },
                    "207": { "$ref": "#/responses/same_name_clash" }
                }
            }
        }
    },
    "responses": {
        "empty_schema": { "description": "empty schema" },
        "regular_schema": {
            "description": "regular schema",
            "schema": { "type": "object", "properties": { "regular_schema": { "type": "string" } } }
        },
        "indirect_schema": {
            "description": "indirect schema",
            "schema": { "$ref": "#/definitions/some_def" }
        },
        "same_name": {
            "description": "same name schema",
            "schema": { "$ref": "#/definitions/same_name" }
        },
        "same_name_clash": {
            "description": "same name clash schema",
            "schema": { "type": "object", "properties": { "same_name_clash_response": { "type": "string" } } }
        }
    },
    "definitions": {
        "direct_schema_ref": { "type": "object", "properties": { "direct_schema_ref": { "type": "string" } } },
        "some_def": { "type": "object", "properties": { "some_def": { "type": "string" } } },
        "same_name": { "type": "object", "properties": { "same_name": { "type": "string" } } },
        "same_name_clash": { "type": "object", "properties": { "same_name_clash_definition": { "type": "string" } } }
    }
}
```
gets normalized to:
```json
{
  "swagger": "2.0",
  "info": {
    "version": "0.0.1",
    "title": "your title"
  },
  "paths": {
    "/persons": {
      "get": {
        "operationId": "get_person",
        "description": "Gets `Person` object.",
        "responses": {
          "200": { "description": "empty schema" },
          "201": {
            "description": "inline schema",
            "schema": { "$ref": "#/definitions/Response_get_person_201" }
          },
          "202": {
            "description": "inline schema ref",
            "schema": { "$ref": "#/definitions/direct_schema_ref" }
          },
          "203": { "description": "empty schema" },
          "204": {
            "description": "regular schema",
            "schema": { "$ref": "#/definitions/regular_schema" }
          },
          "205": {
            "description": "indirect schema",
            "schema": { "$ref": "#/definitions/some_def" }
          },
          "206": {
            "description": "same name schema",
            "schema": { "$ref": "#/definitions/same_name" }
          },
          "207": {
            "description": "same name clash schema",
            "schema": { "$ref": "#/definitions/Response_same_name_clash" }
          }
        }
      }
    }
  },
  "definitions": {
    "direct_schema_ref": {
      "type": "object",
      "properties": {
        "direct_schema_ref": { "type": "string" }
      }
    },
    "some_def": {
      "type": "object",
      "properties": {
        "some_def": { "type": "string" }
      }
    },
    "same_name": {
      "type": "object",
      "properties": {
        "same_name": { "type": "string" }
      }
    },
    "same_name_clash": {
      "type": "object",
      "properties": {
        "same_name_clash_definition": { "type": "string" }
      }
    },
    "regular_schema": {
      "type": "object",
      "properties": {
        "regular_schema": { "type": "string" }
      }
    },
    "Response_same_name_clash": {
      "type": "object",
      "properties": {
        "same_name_clash_response": { "type": "string" }
      }
    },
    "Response_get_person_201": {
      "type": "object",
      "properties": {
        "inline_schema": { "type": "string" }
      }
    }
  }
}
```

* inline response types will be removed and be replaced an explicit response type definition (either using an existing response type name) or generating it for the corresponding endpoint, method, and response code
* this will also improve the response type definitions from something like `ResponseWithBody < 200, object >` to a more meaningful declaration like: `ResponseWithBody < 200, Response_findById_200 >`
@Kosta-Github
Copy link
Author

@Markionium can you please check again?

@Markionium
Copy link
Collaborator

Thanks a lot!

@Markionium Markionium merged commit 0d16ff4 into mtennoe:master Jul 17, 2019
@Markionium
Copy link
Collaborator

Published as 1.11.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants