Problem: OpenAPI schema doesn't use URIs to identify resources #3561
Conversation
|
Hello @dkliban! Thanks for updating the PR. Cheers ! There are no PEP8 issues in this Pull Request. 🍻 Comment last updated on August 30, 2018 at 13:27 Hours UTC |
| def get_resource_description(self, name, example_uri): | ||
| """Return a description for the resource associated with the viewset. | ||
| :param type view: the view class for which a description is desired. | ||
| :return: description of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use google/napolean style docstrings
| :param bool public: if True, all endpoints are included regardless of access through `request` | ||
| :returns: the :class:`.Paths` object and the longest common path prefix, as a 2-tuple | ||
| :rtype: tuple[openapi.Paths,str] | ||
| """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use google/napolean style docstrings
|
This is a subset of the schema generated from this code How would any client generated from this schema know which path is being accessed? Since |
|
I don't know much about openapi, and I could be totally wrong about this. I think the human interacting with the autogenerated client would know what should go there from the description like |
|
The operationId provides each method in the generated bindings with a name. So the user of the bindings knows exactly what to specify for the parameter. Is that what you were asking? |
|
@werwty Or were you talking about validation of the response? I think that is what the schema response objects are used for. Artifact does not currently have one specified, but most of the others do. Like hrere: https://github.com/dkliban/pulp3-python-bindings/blob/href-as-identifier/swagger_client/api/remotes_api.py#L127 |
Ah, I just looked into the bindings code and you are right. The bindings does not use the path to generate the swagger_client.api objects. How would validation on the urls be done? Can I pass any url to {artifact_href}? I also ran the generated schema code through a validator, and swagger does not like equivalent paths. There are a couple of issues open discussing this, but it doesn't appear that they're going to support it in the schema specification: |
I agree. I found another RFE that worries me: OAI/OpenAPI-Specification#577. Edit: I do think the bindings though would validate the response and raise an error if the server returned something other than an artifact. |
|
@werwty Thank you for looking into the schema validation. I agree that we should try to produce a schema that is OpenAPI compliant. I have done some reading in this area and have found that there is a desire (mostly from Google) to improve the OpenAPI 3 spec to include either an ability to have path parameters that have '/' in them[0] or add ability to specify path parameters of type 'hypermedia'[1]. Another proposal is to have OpenAPI be able to keep track of resources[2]. With all that said, these are only proposals at this time and we cannot use any of the methods from above to describe our API at this time. In reading all these proposals it became clear that the problem with the shema this PR is generating is that it it is too obscure for generating server side code that would be matching URLs. However, this schema is not a problem for generating docs and bindings. I also think that @timburks said it best here[3]. Perhaps we should be more liberal about what WE consider valid OpenAPI schema? [0] OAI/OpenAPI-Specification#1459 |
|
Maybe we can accept ids and hrefs. I was thinking about how that would work, and there are some questions I have. Let's assume we don't merge this PR and let the openAPI v2 schema describe the API with IDs. Here's my question. Currently we return task data like: For the user to use that resulting data with an API then need to parse the url to get the two expected parameters (repo=1, version=3). We could do something like this: As yet a third alternative, we could do what the github API does which is return the entire sub-resource serialized. That would be like: In thinking about this, my primary goals are:
What do you think we should do? |
|
@bmbouter the more I think about the hrefs vs id debate, the more I think a solution like one of the ones you propose makes the best sense. 👍 from me. |
|
+1 option 2. Let's return the URI and the parameters (but not the fully serialized sub-resource, just enough to identify what the sub-resource is)
👍
👍 Edit:
I was confused and thought you were talking about references to other objects (like publish needing a reference to a repository.) Re-reading this I see your concern is with path parameters, which has to take in the id to construct the url like in /repository/{id}. I think it's fair not to support referencing via url in this case, since bindings users will probably be using/storing the id as a reference. I think it's ok to support full on HATEOAS style API for the REST API users, but let tools that integrate with pulp store non href identifiers. |
|
Correct me if I am wrong but accepting id and href would mean we don't have to define generators to alter the schema nor the bindings? We could use them as they are? |
|
@daviddavis With this PR option, either path would allow us to use the generic bindings and generic generators. I can see there is much support for a compromise. I'm outlining this compromising position to expore it, but I don't think it's as awesome as just using hrefs and accepting this PR. I want to clearly state that. By going the ID route we're trading a simpler API (one way to represent resources) for a more complex, but compliant one. When presented w/ this choice, many Google teams decided to do something that is less compliant, but lower complexity. In software I think complexity is the real enemy, so we should do the same. Also the bindings this PR produces look extremely usable (to me). I believe only with full support form @daviddavis and @werwty and @jlsherrill could I hope this could still be Pulp's API future. Otherwise we'll end up with a compromise. I'm trying to make a case for this API approach on its merit. It's hard though because IDs are probably how everyone has done projects before (including me). The merit of this API approach is that using hrefs produces a lower complexity API, and that is a better API. Are IDs really great or are they just familiar? Is openAPI v2's decision decision of how the APIs work really the one for Pulp? I suggest the answer is no and that we should go the way of least complexity instead. |
|
In all cases (option 1, 2, or 3) how does the user handle the multiple object types that could be returned by |
|
@bmbouter I think @daviddavis was talking about the generator class that I wrote in this PR. @daviddavis I think that we still need to write a custom schema generator to expose all the right docs. I am thinking of the 'repository_pk' and 'number' path parameters that don't have docs right now. https://docs.pulpproject.org/en/3.0/nightly/integration-guide/rest-api/index.html#get--repositories-repository_pk-versions-number- Perhaps there is another way to fix it that I am not aware of. |
|
@bmbouter In all cases the user will need process the response and decide what to do with it. For both options 1 and 2, I don't think we'll be able to describe using OpenAPI 2.0 a schema that returns 1 of 2 different responses. I don't think there is that flexibility in the spec. In the case of this PR, the bindings generated from this PR don't provide any machinery for converting an href into an API client for that resource type. The user will still have to write code that determines what kind of API client needs to be used to access this resource. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema generator creates a schema that is less openAPI compliant and is intended to support a specific API use case. Namely to support generating bindings with swagger. The custom generator is a great approach but it should not be associated to the (main) /api endpoint. We should add another endpoint that is backed by the custom generator that can be used for swagger.
|
@jortel I'm confused by the suggestion to create two openAPI schemas. How can we describe one API two ways? The plan just sent to pulp-dev says that we're not supporting IDs and only hrefs. Assuming that's the plan, this PR is the only way openAPI can describe an API like that that I know of. Am I thinking about this right? |
|
It's my understanding that the custom schema generator proposed here generates a schema with paths look like: instead of: to accommodate swagger. Is my understanding incorrect? Perhaps a better, clearer suggestion would be: instead of a different endpoint, how about a different format. Eg: /docs/api/?format=openapi-for-swagger |
|
The |
|
@daviddavis, gotcha. |
|
After getting a better understanding of this from @bmbouter, I no longer have the concern about the proposed generated schema at the However, I'd like to suggest an improvement in the and I think it would be clearer if the For example: |
|
@jortel I am concerned that if we add a format of the href, we will then need to once again describe what the 'id' field is. Or what the 'repository_pk' and 'version_number' are for the repo version href. |
d6fd336
to
aa2b48c
Compare
aa2b48c
to
a28b440
Compare
a28b440
to
fab27a5
Compare
Codecov Report
@@ Coverage Diff @@
## master #3561 +/- ##
==========================================
- Coverage 57.71% 57.05% -0.66%
==========================================
Files 60 61 +1
Lines 2521 2571 +50
==========================================
+ Hits 1455 1467 +12
- Misses 1066 1104 +38
Continue to review full report at Codecov.
|
697be76
to
b3c7b82
Compare
|
I was able to generate the bindings using this schema. I then wrote the following script that uses the python bindings to create a remote, a repository, and then sync the remote into the repository. |
Codecov Report
@@ Coverage Diff @@
## master #3561 +/- ##
==========================================
- Coverage 57.7% 56.75% -0.96%
==========================================
Files 60 61 +1
Lines 2523 2592 +69
==========================================
+ Hits 1456 1471 +15
- Misses 1067 1121 +54
Continue to review full report at Codecov.
|
|
@dkliban thanks for the code example. It's helpful. I see you calling this: remote_href = urlparse(api_response.href).pathAfter we return relative URI, it will just be this?: remote_href = api_response.href |
|
@daviddavis Yes, that is exactly correct. urlparse is only needed because we are returning a full URL. |
|
Looks like there are problems with the pulp_ansible plugin. There's a view that returns a static dict the Galaxy CLI requires when looking at the API's versions. There's no queryset needed since the response is static and so this line raises a exception: |
f421046
to
a01445d
Compare
Solution: Create a custom OpenAPI generator that refers to resources by their URI. closes pulp#3856 https://pulp.plan.io/issues/3856 closes pulp#3851 https://pulp.plan.io/issues/3851
a01445d
to
b9399bc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worked and code LGTM 👍
Solution: Create a custom OpenAPI generator that refers to resources by their URI.
To see the Docs generated with this change, point the browser to http://:8000/pulp/api/v3/docs/
The JSON schema that can be used to generate bindings can be downloaded at http://localhost:8000/pulp/api/v3/docs/api?format=openapi