Hello,
A couple of Quarkus contributors discussed offline contract-first API development and how it can be achieved in Quarkus.
In this mail, I will try to capture the result of these discussions; it's not an easy task, they got pretty chatty about it. But let's try. Bear with me; I'm not an expert on the topic; I'm just the messenger.
To simplify and scope the conversation, we will only focus on REST. The principles can be applied to the other type of interactions (event-driven, GraphQL, and so on)
What's contract-first API development?
The idea behind contract-first API development is to have a set of API descriptors that drive your development. They are generally OpenAPI descriptors in the REST world, but that's not the only format (we will talk about that later).
Immediately, you can imagine that contract first API development for Quarkus would be to generate a project skeleton based on an OpenAPI descriptor. The generation produces the project structure, the resource skeleton, and the data structures that are part of the API contract (body, entity...).
That is nice, but, well, Quarkus deserves better ;-)
Client-Side generation
What can be done for the server can be done for the client. In most distributed systems, you need to call remote endpoint. Here, the idea would be to generate the (MicroProfile) Rest Client interface from an OpenAPI descriptor. So, you don't have to write the interface yourself. As for the server-side, it would generate the Rest Client interface (but this time, no need to implement anything) and the data structures. It can even introduce the proper configuration attribute in your "application.properties" to configure the URL.
How can this be done?
Generating code from the OpenAPI descriptor is not something new. In addition, the Apicurio project (https://www.apicur.io/) started, as part of the Apicurio Studio, to support the server-side generation. Granted, it may be a bit outdated, but the base is there (Eric, if you are around, maybe you can give us an update).
Trigging the generation can be done either from the dev console or via the "qs" cli.
Ok, but can't we go further?
Mocking Services
From here, we are more talking about ideas than concrete implementations (it was the case before, but it was easy to see how it could be implemented).
When implementing an application requiring a service, you end up having to call that service. But what if it's not yet implemented or available? What could be done is to generate a mock version from the OpenAPI descriptor. Maybe, some fixtures can be required, but if the OpenAPI descriptor is complete enough (with examples and so on), we have everything to run a mock version of the service. So, if you use dev services, it would just start that fake version and configure your application to call it. So, you are not stuck and can start implementing your business logic. Of course, as soon as the service is ready, you would not use the mock version.
Conformance Testing
Back to the server-side. Even though you generated the skeleton, things can quickly drift. Again, if the OpenAPI descriptor is complete enough, Quarkus could run conformance tests against your API.
The purpose of the Pact (https://docs.pact.io/), among other tools, is precisely this. However, Pact does not use OpenAPI as input, which requires a second type of descriptor. Not the end of the world, but the idea would be to avoid that. The Pact documentation explains very well the different purpose (https://docs.pact.io/faq/convinceme#but-i-use-swaggeropenapi)
Thoughts? Ideas?
Clement
--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAKW6ficvQpe15McuaYC2j_3ROMRtfjuZa-dtPz3i4GArkCHs%2BA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAGzaM-41_aGoMQZrQ8orRwV%2B_bnRwEoM_VzkUPc6op7mnehphA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAL%3DE%3DjQtevm05EkMUvzfNvj%3DyFiwX4B%2BZpjmU8GhhzeM1ww%3DzA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAL%3DE%3DjQtevm05EkMUvzfNvj%3DyFiwX4B%2BZpjmU8GhhzeM1ww%3DzA%40mail.gmail.com.
Hello,
A couple of Quarkus contributors discussed offline contract-first API development and how it can be achieved in Quarkus.
In this mail, I will try to capture the result of these discussions; it's not an easy task, they got pretty chatty about it. But let's try. Bear with me; I'm not an expert on the topic; I'm just the messenger.
To simplify and scope the conversation, we will only focus on REST. The principles can be applied to the other type of interactions (event-driven, GraphQL, and so on)
What's contract-first API development?
The idea behind contract-first API development is to have a set of API descriptors that drive your development. They are generally OpenAPI descriptors in the REST world, but that's not the only format (we will talk about that later).
Immediately, you can imagine that contract first API development for Quarkus would be to generate a project skeleton based on an OpenAPI descriptor. The generation produces the project structure, the resource skeleton, and the data structures that are part of the API contract (body, entity...).
That is nice, but, well, Quarkus deserves better ;-)
Client-Side generation
What can be done for the server can be done for the client. In most distributed systems, you need to call remote endpoint. Here, the idea would be to generate the (MicroProfile) Rest Client interface from an OpenAPI descriptor. So, you don't have to write the interface yourself. As for the server-side, it would generate the Rest Client interface (but this time, no need to implement anything) and the data structures. It can even introduce the proper configuration attribute in your "application.properties" to configure the URL.
How can this be done?
Generating code from the OpenAPI descriptor is not something new. In addition, the Apicurio project (https://www.apicur.io/) started, as part of the Apicurio Studio, to support the server-side generation. Granted, it may be a bit outdated, but the base is there (Eric, if you are around, maybe you can give us an update).
Trigging the generation can be done either from the dev console or via the "qs" cli.
Ok, but can't we go further?
Mocking Services
From here, we are more talking about ideas than concrete implementations (it was the case before, but it was easy to see how it could be implemented).
When implementing an application requiring a service, you end up having to call that service. But what if it's not yet implemented or available? What could be done is to generate a mock version from the OpenAPI descriptor. Maybe, some fixtures can be required, but if the OpenAPI descriptor is complete enough (with examples and so on), we have everything to run a mock version of the service. So, if you use dev services, it would just start that fake version and configure your application to call it. So, you are not stuck and can start implementing your business logic. Of course, as soon as the service is ready, you would not use the mock version.
Conformance Testing
Back to the server-side. Even though you generated the skeleton, things can quickly drift. Again, if the OpenAPI descriptor is complete enough, Quarkus could run conformance tests against your API.
The purpose of the Pact (https://docs.pact.io/), among other tools, is precisely this. However, Pact does not use OpenAPI as input, which requires a second type of descriptor. Not the end of the world, but the idea would be to avoid that. The Pact documentation explains very well the different purpose (https://docs.pact.io/faq/convinceme#but-i-use-swaggeropenapi)
Thoughts? Ideas?
Clement
Mocking Services
From here, we are more talking about ideas than concrete implementations (it was the case before, but it was easy to see how it could be implemented).
When implementing an application requiring a service, you end up having to call that service. But what if it's not yet implemented or available? What could be done is to generate a mock version from the OpenAPI descriptor. Maybe, some fixtures can be required, but if the OpenAPI descriptor is complete enough (with examples and so on), we have everything to run a mock version of the service. So, if you use dev services, it would just start that fake version and configure your application to call it. So, you are not stuck and can start implementing your business logic. Of course, as soon as the service is ready, you would not use the mock version.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CALeTM-nX1hKddGpwktD6Ly87iS3woEtWpQUEKpMdyXSN9-7M0w%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAL%3DE%3DjQtevm05EkMUvzfNvj%3DyFiwX4B%2BZpjmU8GhhzeM1ww%3DzA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CANhb1xYJxtDx8r6gLgyYDLYEwMFYwJPCUryGJWGmMKdxcYuj1w%40mail.gmail.com.
Basic query/reply
Matching replies from queries emitted by the service
Creation of matching expectationThe possibility to create expectation of incoming queries in the mocking system
Record/replayPossibility to record traffic to ease the creation of mock to be replayed later.
Performance/load testingLast but not least : Internally we created a ghosting solution based on Vertx because we needed a mocking solution that literally scales during the load testing of our applications.
There shouldn't need to be a dev console or cli step here.
At least when doing service side contract first development.
Record/replay
Possibility to record traffic to ease the creation of mock to be replayed later.
This is very interesting, and something I thought about too, but you have to have real data to record first, so where would that happen? The user using curl? Using the SwaggerUI? Recording production exchanges? That's what I haven't been able to figure out. But yeah, something that looks at exchanges "somewhere" and spits out HTTP fixtures would be awesome.
Performance/load testing
Last but not least : Internally we created a ghosting solution based on Vertx because we needed a mocking solution that literally scales during the load testing of our applications.
You want to test the load, or you want the mocking to scale under load?
--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAKU9E9uZOrap5SGtMp-eXU%3Dv9BftSbK0JPtweUe_qXODGCQHTg%40mail.gmail.com.
Hello,
Thanks for all these ideas! Let's try to organize them a little bit and see where we can go from there.
From these discussions, there are two facets to this work:
These are pretty different, so let's cover them separately.
Server/Client generation based on OpenAPI
For the server-side, the problem is simple, at least well-known. You provide an OpenAPI descriptor, and it bootstraps the server implementation (Jax-RS resources and data objects). There are already plenty of generators like this, but I will come to that a bit later.
The client-side generation is less mature, or at least not as common. The idea is to generate the Microprofile REST Client interface based on an OpenAPI descriptor. It is similar to server-side generation. Granted, if you want to create the REST Client interface for an extremely extensive API (Github or Kubernetes), it may not be practical. However, if the service you target had a dozen of endpoints, that sounds possible. Solutions such as filtering by prefix could also help. The advantage here is to ease the writing of such REST clients, which while being marvelous, are still new.
As I said before, there are plenty of generators, primarily targetting the server-side. I discussed this quickly with Eric from the Apicurio project, and we will work on an extension that handles the server and client generation, as there is a lot of code shared between these two tasks.
Faking services
The second side of the work is quite different, but it also can be a significant differentiator. The idea is to provide developers a feature to invoke services that do not yet exist or are not accessible. You can see it as a mock of the remote service or a fake service.
There are multiple ways to achieve this:
All these approaches are not incompatible, and they can all complement each other.
The first approach is slightly similar to what Wiremock or Prism provides. You write a spec (or code it) that describes the stub, and potentially, some verification rules and dev services can run the service for you. In dev or test modes, you can run your application and call that service.
The second approach is somewhat similar. However, instead of writing the descriptor, it would take an OpenAPI descriptor as input. Microcks does this, for example. If your OpenAPI descriptor contains enough details about the service, it can produce fake responses. Yes, you may have a bit less control over the HTTP responses, but it's a smooth setup (especially combined with the client-side generation mentioned above).
The third approach is in the same vein. However, this time, the behavior of the fake service is recorded. It means that the service runs somewhere, and you were able to record the request/response. Multiple tools are doing this, such as Pact, Wiremock (yes, it seems to be a new feature); it's also on the roadmap for Microcks.
Another aspect that Microcks and Wiremocks provide is the ability to control the latency between your application and the fake service. I'm a big fan of that feature, as it let you verify your resilience strategy (timeout, retry, circuit-breaker, and so on)
I don't know the landscape enough, but it seems that multiple tools already exist, so we should select and integrate.
Conformance
That's also an interesting topic. Conformance would allow verifying that you implement a service correctly or that the service you are calling behaves as expected. Microcks supports this. It takes the OpenAPI descriptor as input and verifies that the service implements it correctly (it also supports versions, as it is very likely to have multiple versions of a service).
Last words
This email focuses solely on HTTP/REST. But as highlighted by Laurent, the same principles would work for event-driven architectures.
For the server/client-side generation, there is a plan. The second part is more challenging but looks very promising. It would fit very well with the developer experience Quarkus provides.
Clement
Hello,
Thanks for all these ideas! Let's try to organize them a little bit and see where we can go from there.
From these discussions, there are two facets to this work:
- server/client generation based on OpenAPI description
- mock / record-replay services
These are pretty different, so let's cover them separately.
Server/Client generation based on OpenAPI
For the server-side, the problem is simple, at least well-known. You provide an OpenAPI descriptor, and it bootstraps the server implementation (Jax-RS resources and data objects). There are already plenty of generators like this, but I will come to that a bit later.
The client-side generation is less mature, or at least not as common. The idea is to generate the Microprofile REST Client interface based on an OpenAPI descriptor. It is similar to server-side generation. Granted, if you want to create the REST Client interface for an extremely extensive API (Github or Kubernetes), it may not be practical. However, if the service you target had a dozen of endpoints, that sounds possible. Solutions such as filtering by prefix could also help. The advantage here is to ease the writing of such REST clients, which while being marvelous, are still new.
As I said before, there are plenty of generators, primarily targetting the server-side. I discussed this quickly with Eric from the Apicurio project, and we will work on an extension that handles the server and client generation, as there is a lot of code shared between these two tasks.
Faking services
The second side of the work is quite different, but it also can be a significant differentiator. The idea is to provide developers a feature to invoke services that do not yet exist or are not accessible. You can see it as a mock of the remote service or a fake service.
There are multiple ways to achieve this:
- describing the API in the code with expectations,
- running the fake service based on its OpenAPI description,
- recording service interactions (in production, for example) and replaying them.
All these approaches are not incompatible, and they can all complement each other.
The first approach is slightly similar to what Wiremock or Prism provides. You write a spec (or code it) that describes the stub, and potentially, some verification rules and dev services can run the service for you. In dev or test modes, you can run your application and call that service.
The second approach is somewhat similar. However, instead of writing the descriptor, it would take an OpenAPI descriptor as input. Microcks does this, for example. If your OpenAPI descriptor contains enough details about the service, it can produce fake responses. Yes, you may have a bit less control over the HTTP responses, but it's a smooth setup (especially combined with the client-side generation mentioned above).
The third approach is in the same vein. However, this time, the behavior of the fake service is recorded. It means that the service runs somewhere, and you were able to record the request/response. Multiple tools are doing this, such as Pact, Wiremock (yes, it seems to be a new feature); it's also on the roadmap for Microcks.
Another aspect that Microcks and Wiremocks provide is the ability to control the latency between your application and the fake service. I'm a big fan of that feature, as it let you verify your resilience strategy (timeout, retry, circuit-breaker, and so on)
I don't know the landscape enough, but it seems that multiple tools already exist, so we should select and integrate.
Conformance
That's also an interesting topic. Conformance would allow verifying that you implement a service correctly or that the service you are calling behaves as expected. Microcks supports this. It takes the OpenAPI descriptor as input and verifies that the service implements it correctly (it also supports versions, as it is very likely to have multiple versions of a service).
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAKW6ficaBuRqao2o8-fh3AMV-sm6Ah67QuvAScgsQETsjNMmsQ%40mail.gmail.com.
On Fri, 7 May 2021 at 01:49, clement escoffier <clement....@gmail.com> wrote:Hello,
Thanks for all these ideas! Let's try to organize them a little bit and see where we can go from there.
From these discussions, there are two facets to this work:
- server/client generation based on OpenAPI description
- mock / record-replay services
These are pretty different, so let's cover them separately.
Server/Client generation based on OpenAPI
For the server-side, the problem is simple, at least well-known. You provide an OpenAPI descriptor, and it bootstraps the server implementation (Jax-RS resources and data objects). There are already plenty of generators like this, but I will come to that a bit later.
The client-side generation is less mature, or at least not as common. The idea is to generate the Microprofile REST Client interface based on an OpenAPI descriptor. It is similar to server-side generation. Granted, if you want to create the REST Client interface for an extremely extensive API (Github or Kubernetes), it may not be practical. However, if the service you target had a dozen of endpoints, that sounds possible. Solutions such as filtering by prefix could also help. The advantage here is to ease the writing of such REST clients, which while being marvelous, are still new.
As I said before, there are plenty of generators, primarily targetting the server-side. I discussed this quickly with Eric from the Apicurio project, and we will work on an extension that handles the server and client generation, as there is a lot of code shared between these two tasks.
It would be really cool if we could do this from the Dev UI, just point it at a URL, hit a button and you have a fully generated client.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAD%2BL2cx_Vk3iNm0G5cT3cvEQF8p%3Dvrmti9EYoy3E7CEBdFGDBg%40mail.gmail.com.
pt., 7 maj 2021 o 03:31 Stuart Douglas <sdou...@redhat.com> napisał(a):On Fri, 7 May 2021 at 01:49, clement escoffier <clement....@gmail.com> wrote:Hello,
Thanks for all these ideas! Let's try to organize them a little bit and see where we can go from there.
From these discussions, there are two facets to this work:
- server/client generation based on OpenAPI description
- mock / record-replay services
These are pretty different, so let's cover them separately.
Server/Client generation based on OpenAPI
For the server-side, the problem is simple, at least well-known. You provide an OpenAPI descriptor, and it bootstraps the server implementation (Jax-RS resources and data objects). There are already plenty of generators like this, but I will come to that a bit later.
The client-side generation is less mature, or at least not as common. The idea is to generate the Microprofile REST Client interface based on an OpenAPI descriptor. It is similar to server-side generation. Granted, if you want to create the REST Client interface for an extremely extensive API (Github or Kubernetes), it may not be practical. However, if the service you target had a dozen of endpoints, that sounds possible. Solutions such as filtering by prefix could also help. The advantage here is to ease the writing of such REST clients, which while being marvelous, are still new.
As I said before, there are plenty of generators, primarily targetting the server-side. I discussed this quickly with Eric from the Apicurio project, and we will work on an extension that handles the server and client generation, as there is a lot of code shared between these two tasks.
It would be really cool if we could do this from the Dev UI, just point it at a URL, hit a button and you have a fully generated client.
When it comes to client generation from OpenAPI I'd like to add a few points here that could be taken into consideration when implementing this feature. I have been doing something like that in my project based on an open api tools generator and I think that the most important thing is that it should only generate methods on operations that are to be used. So at least there should be an option to define what operation ids or paths+http methods you are interested in. This significantly helps when working with big OpenAPI definitions.In addition to that here are some points about the generated methods for clients- generate at least two methods - one with all parameters and second one with just required parameters - in many cases the later would be most frequently used and makes it way simpler to be used. There might be lots of query params that are optional so always requiring them can be not the best.- take into account security schemes especially for those endpoints that use query param based auth like api key etc. That should be part of the generated method so it can be easily given when being invoked.- providing some pluggable security schemes support for rest calls based on generated clients can be properly authenticated like adding http headers. Sometimes this can be simple using basic auth but might be more complex when using various OAuth flows- optional generation of the data model as data model might be already part of the application itself or its dependencies - here is a bit tricky part as it somehow needs to provide model to the generated client interface so maybe relying on jandex to check if given model is already there or similarThese are the main pain points I ran into when generating MP Rest Client interfaces from OpenAPI definition. Hope this can be a useful input for the upcoming implementation of this very useful feature.
On 7 May 2021, at 3:31, Stuart Douglas wrote:
It would be really cool if we could do this from the Dev UI, just point it at a URL, hit a button and you have a fully generated client.
am I the only one that think this is the least interesting option :)
But if this dev ui is just using the info from application.properties or annotations defining which endpoints you are after then it would be nice
to have the button to (re)generate.
/max
https://xam.dk/about
Hello,
Thanks for all these ideas! Let's try to organize them a little bit and see where we can go from there.
From these discussions, there are two facets to this work:
- server/client generation based on OpenAPI description
- mock / record-replay services
These are pretty different, so let's cover them separately.
Server/Client generation based on OpenAPI
For the server-side, the problem is simple, at least well-known. You provide an OpenAPI descriptor, and it bootstraps the server implementation (Jax-RS resources and data objects). There are already plenty of generators like this, but I will come to that a bit later.
The client-side generation is less mature, or at least not as common. The idea is to generate the Microprofile REST Client interface based on an OpenAPI descriptor. It is similar to server-side generation. Granted, if you want to create the REST Client interface for an extremely extensive API (Github or Kubernetes), it may not be practical. However, if the service you target had a dozen of endpoints, that sounds possible. Solutions such as filtering by prefix could also help. The advantage here is to ease the writing of such REST clients, which while being marvelous, are still new.
As I said before, there are plenty of generators, primarily targetting the server-side. I discussed this quickly with Eric from the Apicurio project, and we will work on an extension that handles the server and client generation, as there is a lot of code shared between these two tasks.
Faking services
The second side of the work is quite different, but it also can be a significant differentiator. The idea is to provide developers a feature to invoke services that do not yet exist or are not accessible. You can see it as a mock of the remote service or a fake service.
There are multiple ways to achieve this:
- describing the API in the code with expectations,
- running the fake service based on its OpenAPI description,
- recording service interactions (in production, for example) and replaying them.
All these approaches are not incompatible, and they can all complement each other.
The first approach is slightly similar to what Wiremock or Prism provides. You write a spec (or code it) that describes the stub, and potentially, some verification rules and dev services can run the service for you. In dev or test modes, you can run your application and call that service.
The second approach is somewhat similar. However, instead of writing the descriptor, it would take an OpenAPI descriptor as input. Microcks does this, for example. If your OpenAPI descriptor contains enough details about the service, it can produce fake responses. Yes, you may have a bit less control over the HTTP responses, but it's a smooth setup (especially combined with the client-side generation mentioned above).
The third approach is in the same vein. However, this time, the behavior of the fake service is recorded. It means that the service runs somewhere, and you were able to record the request/response. Multiple tools are doing this, such as Pact, Wiremock (yes, it seems to be a new feature); it's also on the roadmap for Microcks.
Another aspect that Microcks and Wiremocks provide is the ability to control the latency between your application and the fake service. I'm a big fan of that feature, as it let you verify your resilience strategy (timeout, retry, circuit-breaker, and so on)
I don't know the landscape enough, but it seems that multiple tools already exist, so we should select and integrate.
Conformance
That's also an interesting topic. Conformance would allow verifying that you implement a service correctly or that the service you are calling behaves as expected. Microcks supports this. It takes the OpenAPI descriptor as input and verifies that the service implements it correctly (it also supports versions, as it is very likely to have multiple versions of a service).
Last words
This email focuses solely on HTTP/REST. But as highlighted by Laurent, the same principles would work for event-driven architectures.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/f9c640b9-e812-4a54-b925-0a59c9492c4bn%40googlegroups.com.