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

Get id (UUID) of group created just now #10

Open
sachingaikwad123 opened this issue Oct 21, 2024 · 7 comments
Open

Get id (UUID) of group created just now #10

sachingaikwad123 opened this issue Oct 21, 2024 · 7 comments
Labels
enhancement New feature or request gathering-interest

Comments

@sachingaikwad123
Copy link

First of all: This is amazing package! I am loving it! Thank you so much!

Problem: I am creating 'group' with client.groups.post(). This creates group successfully. Keycloak 'POST' rest apis (especially create objects) ones are weird because they do not return object created just now. Typically, I have seen the 'create object' REST APIs return the object create just now. Due to this, clients do not get to know 'group id' (UUID) generated by keycloak for this newly created group. I want to get this 'keycloak group id' (UUID) so that I can reference this in my all future requests.

How to get this 'keycloak group id' (UUID)?

  1. I can do search by group name: But group search is not great for Keycloak. Because of nested groups, I may get 2 groups in search results. So, I don't want to rely on this.

  2. Keycloak itself provides this 'keycloak group id' UUID (or any other ID generated by keycloak) in 'HTTP Response Header': 'Location'. I see that 'python-keycloak' is getting this 'keycloak group id' from that place and returning that from 'create_group' method.

Solution:

  1. Provide a way in this library to access 'Response Headers'
  2. Provide a helper function in this library which takes 'Response Headers' and extracts 'UUID' from last part of URL returned.

This solution (1) will keep this library functionality generic and provide access to any 'Response Headers'. (2) is just a helper for this specific use-case so that not everyone has to write 'extract id from Location' function.

I am sure Keycloak improve their REST API to fix this problem (as they mention about this problem in their issue description here: keycloak/keycloak#8983

Thoughts?

@derlin
Copy link
Owner

derlin commented Oct 21, 2024

Hello @sachingaikwad123 and first of all, thank you for the kind words and the interest 🤗

This is a very interesting idea and functionality, though tricky to make seamless and coherent with the current implementation.

Instead of limiting this to headers, I am thinking of adding a way to let users skip the automatic parsing and get back the raw response object. This way, you can access headers and anything else if you need to. However, in case you need both headers and body, it would be on you to convert the body to json (e.g. using resp.json()).

I would need to sleep on it, but it could be something like:

# 1) either a chainable modifier, 
client.groups.raw_response.post()
# 2) or a parameter to the client constructor, with a way to "clone" the current client easily
client.with_parsing_off().groups.post()
# 3) or a parameter in the final method
client.groups.post(return_raw_response=True)

Not completely convinced, and the typing will become tricky...

I need to sleep on it, but for now, what do you think of a "return response" feature? Let me know if you have any further thought!

@sachingaikwad123
Copy link
Author

I like the idea of returning 'Response' itself. This will keep things more generic nature. Callers can figure out what they need do by looking at 'Response'.

# 3) or a parameter in the final method
client.groups.post(return_raw_response=True)

I think this 3rd option looks good to me. If we keep 'return_raw_response' optional, it will still keep api calls concise (less words to write) for most of the people. Whoever needs 'response' in very few cases can pass 'return_raw_response' optional param and get the response.

@derlin
Copy link
Owner

derlin commented Oct 27, 2024

@sachingaikwad123 In the meantime, slumber has actually a hidden attribute that stores the last response on a resource: you can get the response using the _ attribute after a resource call. It only requires you to keep a reference to the resource before the get/post/... call.

Example:

from mantelo import KeycloakAdmin

clien = KeycloakAdmin.from_username_password(...)
groups_endpoint = client.groups
groups_endpoint.post({"name": "xx"})

# last response is stored in the `_` attribute:
groups_endpoint._.headers["location"]
# => 'http://localhost:9090/admin/realms/my-realm/groups/73a2abf9-3797-433f-99c6-304fa4b2c961'
groups_endpoint._.request.method
# => POST

# the last response is updated on any subsequent call
groups_endpoint.get()
groups_endpoint._.request.method
# => GET

Would this be enough for you for now?
Implementing what we talked about would actually require some changes in slumber itself to make it a nice/generic implementation, and I don't know how opened they would be about that.

@sachingaikwad123
Copy link
Author

@sachingaikwad123 In the meantime, slumber has actually a hidden attribute that stores the last response on a resource: you can get the response using the _ attribute after a resource call. It only requires you to keep a reference to the resource before the get/post/... call.

Would this be enough for you for now? Implementing what we talked about would actually require some changes in slumber itself to make it a nice/generic implementation, and I don't know how opened they would be about that.

This is amazing. For now, this is sufficient for my implementation. I will go ahead with using "_" attribute and get response right after the call. I don't plan to do any parallel async calls, so I think this should work for now.

Thanks!

@derlin
Copy link
Owner

derlin commented Oct 27, 2024

@sachingaikwad123 FWI, a possible implementation of what we discussed in in branch 10-return-raw. It is definitely a hack, so I am still wondering if I want to commit it. But happy to learn this doesn't block you!

@derlin
Copy link
Owner

derlin commented Oct 27, 2024

I don't plan to do any parallel async calls, so I think this should work for now.

Just know that every time you call client.users you get a new resource object, so you can have multiple calls in parallel if you need, as long as you do not reuse the same reference twice.

a = client.users
b = client.users

a.get()
b.get()

# Each holds its own response
a._ != b._

@sachingaikwad123
Copy link
Author

I don't plan to do any parallel async calls, so I think this should work for now.

Just know that every time you call client.users you get a new resource object, so you can have multiple calls in parallel if you need, as long as you do not reuse the same reference twice.

Oh, nice! That's good to know. Thanks!

@derlin derlin added enhancement New feature or request gathering-interest labels Oct 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request gathering-interest
Projects
None yet
Development

No branches or pull requests

2 participants