Skip to content

POC of how we could develop version 2 of the admin REST API#12313

Closed
edewit wants to merge 1 commit intokeycloak:mainfrom
edewit:open-api
Closed

POC of how we could develop version 2 of the admin REST API#12313
edewit wants to merge 1 commit intokeycloak:mainfrom
edewit:open-api

Conversation

@edewit
Copy link
Copy Markdown
Contributor

@edewit edewit commented Jun 2, 2022

This is not meant to be merged. It's just a POC of how we could build the next version of the Admin REST Api. This doesn't have to be a big bang change we can change it per section for example I create a example of just clients. Doing it this way where we create the open-api first will ensure the documentation is always up to date and that no implementation specific details slip into the API.

Based on this the Admin UI could generate a client that to do the backend calls removing the need for manually maintaining keycloak-nodejs-admin-client

WDYT?

@edewit edewit mentioned this pull request Jun 3, 2022
@edewit edewit force-pushed the open-api branch 4 times, most recently from 8eaf6e2 to d8e3dc7 Compare June 13, 2022 15:22
@jonkoops
Copy link
Copy Markdown
Contributor

I like the approach of going schema first 👍

@@ -0,0 +1,271 @@
openapi: 3.0.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

May be the latest 3.1.0 OpenAPI version can be used or at least version 3.0.3 which is supported by most of modern tools.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure

@stianst
Copy link
Copy Markdown
Contributor

stianst commented Jun 22, 2022

+1 To design first approach here, but would be good to start with a design discussion around a new version of the admin endpoints.

@hmlnarik
Copy link
Copy Markdown
Contributor

Thank you for the draft PR.

One comment re the clients.yaml file - the schemata should be generated from code and not handcrafted. This may or may not be generated directly from the storage representation of particular type (clients in this case).

@edewit
Copy link
Copy Markdown
Contributor Author

edewit commented Jun 23, 2022

@hmlnarik My idea was actually to do the opposite, generate the schema by hand and let the Java classes be generated like how it's done in this PR. Then all that needs to be done is call the storage API from one class that implements this REST interface as seen in here. There are a couple of advantages to this approach IMO 1. There is no way to update the REST endpoints without also creating a schema and documentation for it. 2. There is less chance of implementation specific stuff. For example right now all the *Representations object have properties that are optional even things that cannot be optional like an ID. This is because they are generated from Java types that where a String can be null. Another example is that types get reused, as results when not all fields are filled (e.g. briefRepresentation) for users of the API this is very unclear why they sometimes get an entire object and sometimes only a part. When we specify this more clearly we can make the life of users easier and it will also be possible to generate clients based on this description.

@hmlnarik
Copy link
Copy Markdown
Contributor

@hmlnarik My idea was actually to do the opposite, generate the schema by hand and let the Java classes be generated like how it's done in this PR.

Depending on the use case, this might or might not work.

  1. If this is about introducing an arbitrary simplified API to cover a specific use case, then this approach would work well. This might work for retrieving e.g. brief representations or simplified writes. Representations are then tied to that API specification, and can be generated from it.
    However then this API is tied to a particular feature (e.g. ability for brief representation of a particular object type), and should be clearly marked as such, and not claim that this is an official Keycloak REST API for managing stored objects.

  2. If this is about client API, the properties must be on par with what is stored. Otherwise the REST endpoint would be able to expose properties that are not available in the store (thus not storable), or not expose properties that might be required for an object to be stored (say username for users, or client ID for clients). Generating Java code from openAPI spec approach is thus not viable, and that must be done the other way round.

Then all that needs to be done is call the storage API from one class that implements this REST interface as seen in here. There are a couple of advantages to this approach IMO 1. There is no way to update the REST endpoints without also creating a schema and documentation for it. 2. There is less chance of implementation specific stuff. For example right now all the *Representations object have properties that are optional even things that cannot be optional like an ID. This is because they are generated from Java types that where a String can be null. Another example is that types get reused, as results when not all fields are filled (e.g. briefRepresentation) for users of the API this is very unclear why they sometimes get an entire object and sometimes only a part. When we specify this more clearly we can make the life of users easier and it will also be possible to generate clients based on this description.

@edewit
Copy link
Copy Markdown
Contributor Author

edewit commented Jun 24, 2022

If this is about client API, the properties must be on par with what is stored. Otherwise the REST endpoint would be able to expose properties that are not available in the store (thus not storable), or not expose properties that might be required for an object to be stored (say username for users, or client ID for clients). Generating Java code from openAPI spec approach is thus not viable, and that must be done the other way round.

Of course I'm not saying that we should create an openAPI description that is invalid, clearly this would not work. What I am saying is that creating the openAPI description by hand will have 2 benefits it will always be up to date and it will be more descriptive then doing it the other way around.

Right now we are creating an openAPI description based of the Java classes and for people consuming the API it's not clear what to expect when all you have is this description. Just take a look at the questions raised on the keycloak-nodejs-admin-client about types and responses.

If you see another way to create an openAPI description that is better then the one we have now but still generated from the Java classes, where not every property is optional, then I'm all ears. But I'm afraid that will then need a lot of annotations to make things more descriptive and those could be forgotten.

@hmlnarik
Copy link
Copy Markdown
Contributor

hmlnarik commented Jun 24, 2022

Of course I'm not saying that we should create an openAPI description that is invalid, clearly this would not work. What I am saying is that creating the openAPI description by hand will have 2 benefits it will always be up to date [...]

How would you guarantee that it is indeed in sync with store?

When there is a change in store, it can be translated into OpenAPI spec. To illustrate that this is viable let me state that store already translates entities to generate storage code for various technologies. OpenAPI is just another such translation.

[...] and it will be more descriptive then doing it the other way around.

Any description can be generated from the storage representation just the same, as long as it is appropriately placed e.g. in an annotation. This argument is thus not valid.

In case OpenAPI would be crafted manually, this means double manual work - one to update the store entities, another to update Java storage definitions. Is it beneficial to have two steps where one can suffice?

If you see another way to create an openAPI description that is better then the one we have now but still generated from the Java classes, where not every property is optional, then I'm all ears. But I'm afraid that will then need a lot of annotations to make things more descriptive and those could be forgotten.

Please see above. OpenAPI is only one of the ways to expose stored objects to the world above storage. There are layers in between which benefit the same, e.g. validation, generally Java API. Java API can hardly reuse OpenAPI spec, but it can (and does) use annotations.

@edewit edewit closed this Jun 24, 2022
@edewit edewit deleted the open-api branch June 24, 2022 07:16
@edewit
Copy link
Copy Markdown
Contributor Author

edewit commented Jun 24, 2022

Looking forward to a more descriptive openAPI definition in that case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants