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

Make returned responses more consistent #537

Merged
merged 6 commits into from Mar 28, 2020

Conversation

llamasoft
Copy link
Contributor

This is another big PR so apologies in advance. This PR fixes issue #525 and arises from the discussion therein.

This PR does one thing: create a new JSONAdapter and makes it the default. The "JSON adapter" works just like the previous "Request" adapter except the return value uses the following logic:

if response.status_code == 200:
    try:
        return response.json()
    except ValueError:
        pass
return response

The net result is that all client functions now have consistent logic regarding what they return.
As a result, all instances of the following client logic have been removed:

return response.json()

and

if response.status_code == 204:
    return response
else:
    return response.json()

This will likely cause some breaking changes for some users, especially with regards to the direct client.[read,list,write,delete] methods as they will no longer throw a ValueError from assuming the response always contains a JSON body.

@llamasoft llamasoft requested a review from a team as a code owner October 17, 2019 22:25
@update-docs
Copy link

update-docs bot commented Oct 17, 2019

Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would update some of our documentation based on your changes.
See: https://github.com/hvac/hvac/blob/develop/CONTRIBUTING.md#documentation

@codecov-io
Copy link

codecov-io commented Oct 17, 2019

Codecov Report

Merging #537 into develop will decrease coverage by 0.48%.
The diff coverage is 79.15%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #537      +/-   ##
===========================================
- Coverage    83.03%   82.54%   -0.49%     
===========================================
  Files           55       54       -1     
  Lines         2976     2859     -117     
===========================================
- Hits          2471     2360     -111     
+ Misses         505      499       -6
Impacted Files Coverage Δ
hvac/api/system_backend/capabilities.py 21.42% <0%> (+1.42%) ⬆️
hvac/api/auth_methods/ldap.py 100% <100%> (ø) ⬆️
hvac/api/system_backend/audit.py 94.73% <100%> (-0.72%) ⬇️
hvac/api/system_backend/init.py 56.25% <100%> (-2.58%) ⬇️
hvac/api/secrets_engines/aws.py 97.87% <100%> (-0.31%) ⬇️
hvac/api/system_backend/wrapping.py 100% <100%> (ø) ⬆️
hvac/api/system_backend/seal.py 100% <100%> (ø) ⬆️
hvac/api/system_backend/policy.py 100% <100%> (ø) ⬆️
hvac/api/secrets_engines/azure.py 100% <100%> (ø) ⬆️
hvac/api/system_backend/health.py 85.71% <100%> (-1.79%) ⬇️
... and 60 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 15df074...7ebfacd. Read the comment docs.

@llamasoft
Copy link
Contributor Author

I just had the last-second realization that the login method on the Adapter base class doesn't make sense. The request method it depends on is abstract, so we have zero guarantees about the object type it returns. Instead, I've moved the token extraction logic to a separate abstract method, get_login_token.

I also forgot to mention in the original PR that I added an adapter option called ignore_exceptions that optionally overrides the raise_exception parameter passed to request. This allows a way to suppress exceptions and receive a raw Response object (normally not possible because the raise_exception argument isn't accessible).

@llamasoft
Copy link
Contributor Author

I just realized an additional breaking change regarding this PR: the lookup_* methods (e.g. client.secrets.identity.lookup_[entity|group]) now return a Response[204] (truthy) instead of None (falsy) if the lookup returned no results.

Unfortunately, I can't think of a good way to resolve this issue. The Requests Adapter is created from a user-specified class so we can't assume in advance what sort of data type it will return.
Honestly, given that we can't guarantee return types, we probably shouldn't be interacting with response objects at all, including the helpers on read_* methods that return response.get('data').

What are your thoughts on this matter? I can revert the lookup_* to return None on 204 responses, but that logic may end up causing issues with custom adapters.

drewmullen
drewmullen previously approved these changes Oct 24, 2019
Copy link
Member

@drewmullen drewmullen left a comment

Choose a reason for hiding this comment

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

since we use this library for downstream ansible modules, i was mostly concerned with the expected return values; whether or not they change based on the new json adapter. based on initial tests i dont see breaking changes happening to https://github.com/TerryHowe/ansible-modules-hashivault

based on this consideration, this PR has my 👍 . it should definitely be minor or possibly major release though

    print(client.auth.azure.read_config())
    # prev: {'client_id': 'test', 'environment': 'AzurePublicCloud', 'resource': 'test', 'tenant_id': 'test'}
    # new:  {'client_id': 'test', 'environment': 'AzurePublicCloud', 'resource': 'test', 'tenant_id': 'test'}

the following outputs are identical except for request_id (expected)

new `sys.list_auth_method` output #list_auths NEW ''' { "token/": { "accessor": "auth_token_8e77bdb2", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "token based credentials", "local": false, "options": null, "seal_wrap": false, "type": "token", "uuid": "9aae3834-9e4e-3c3c-a891-d34b49f5dabd" }, "azure/": { "accessor": "auth_azure_2154cde7", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "", "local": false, "options": null, "seal_wrap": false, "type": "azure", "uuid": "504b59a1-7d52-fbd1-082f-c1625b165e58" }, "request_id": "62e1e72f-ebf0-0bde-1ef8-f5696135a07f", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "azure/": { "accessor": "auth_azure_2154cde7", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "", "local": false, "options": null, "seal_wrap": false, "type": "azure", "uuid": "504b59a1-7d52-fbd1-082f-c1625b165e58" }, "token/": { "accessor": "auth_token_8e77bdb2", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "token based credentials", "local": false, "options": null, "seal_wrap": false, "type": "token", "uuid": "9aae3834-9e4e-3c3c-a891-d34b49f5dabd" } }, "wrap_info": null, "warnings": null, "auth": null } '''
previous `sys.list_auth_method` output #list auths OLD ''' { "token/": { "accessor": "auth_token_8e77bdb2", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "token based credentials", "local": false, "options": null, "seal_wrap": false, "type": "token", "uuid": "9aae3834-9e4e-3c3c-a891-d34b49f5dabd" }, "azure/": { "accessor": "auth_azure_2154cde7", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "", "local": false, "options": null, "seal_wrap": false, "type": "azure", "uuid": "504b59a1-7d52-fbd1-082f-c1625b165e58" }, "request_id": "7797ff7c-0fcf-8469-083d-146f1cb0d7b9", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "azure/": { "accessor": "auth_azure_2154cde7", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "", "local": false, "options": null, "seal_wrap": false, "type": "azure", "uuid": "504b59a1-7d52-fbd1-082f-c1625b165e58" }, "token/": { "accessor": "auth_token_8e77bdb2", "config": { "default_lease_ttl": 0, "force_no_cache": false, "max_lease_ttl": 0, "token_type": "default-service" }, "description": "token based credentials", "local": false, "options": null, "seal_wrap": false, "type": "token", "uuid": "9aae3834-9e4e-3c3c-a891-d34b49f5dabd" } }, "wrap_info": null, "warnings": null, "auth": null } '''

great work @llamasoft and thanks for the great contribution

@llamasoft
Copy link
Contributor Author

Any word on if response.get('data') should be removed as well? It's used in a few places, but not in others. In my personal experience, it made return values unpredictable. For example, most read_* methods use it, but lookup_* methods don't.

Unfortunately, removing them will be a breaking change for some users.

@jeffwecan
Copy link
Member

Any word on if response.get('data') should be removed as well?

I suppose I feel it makes most sense to return the entire response in all cases. In general, this module is intended to be a pretty basic wrapper for Vault API (at least as far as I'm concerned).

@drewmullen
Copy link
Member

I agree with @jeffwecan . we had explored the idea of making all returns just the data portion but there are some useful values provided from vault that shouldnt be stripped (see below).

#483 (comment)

@Lucas-C
Copy link

Lucas-C commented Mar 11, 2020

This looks like an nice improvement !
What is the status of this PR ? :)

@jeffwecan
Copy link
Member

@Lucas-C: I figure it is about time to get this PR put in place! :D

Will start working on getting it trued back up with the current state of the develop branch...

@jeffwecan jeffwecan self-assigned this Mar 27, 2020
@jeffwecan
Copy link
Member

Planning on merging this in today. To close the loop on returning the entire response dicts versus only the data key: I still want to see use move to the former in all places eventually. However I'd rather tackle that in another PR / release since its most certainly a breaking change (as @llamasoft alluded to).

Copy link
Member

@jeffwecan jeffwecan left a comment

Choose a reason for hiding this comment

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

giphy finally

@jeffwecan jeffwecan merged commit 33c2410 into hvac:develop Mar 28, 2020
@llamasoft llamasoft deleted the json-adapter branch March 31, 2020 16:32
@jeffwecan jeffwecan modified the milestones: 1.0.0, 0.10.1 Apr 7, 2020
@trishankatdatadog
Copy link
Contributor

Okay, this one was really confusing, because the documentation, which really copies doctstrings from the source code, still says to expect requests responses instead of the JSON body for non-204 responses. Should we update the docstrings?

@jeffwecan
Copy link
Member

I would imagine so, and sorry for the added confusion! @trishankatdatadog, I went ahead and quoted your comment into a new issue (#537) and will try to get a review of docstrings, etc. done, and any incorrect return types updated, sometime over the next week or so. (Though contributions along those lines are of course also very welcomed. ☺)

@trishankatdatadog
Copy link
Contributor

I would imagine so, and sorry for the added confusion! @trishankatdatadog, I went ahead and quoted your comment into a new issue (#537) and will try to get a review of docstrings, etc. done, and any incorrect return types updated, sometime over the next week or so. (Though contributions along those lines are of course also very welcomed. ☺)

Thanks! Also wrong link for issue 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change misc Used as a release-drafter "category"
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants