This issue is here to discuss about the feasibility and the possible technical solutions to generate openAPI specs for Kibana endpoints.
Due to the language we are using, this is far from trivial, and way more difficult than it would be done in a compiled language with proper reflection support.
I won't include the global metadata, common types and such in this discussion, as these are more or less constants and do not (I think) represent any technical challenge.
Just for the record, this is what an OpenAPI path spec looks like
{
"/pets": {
"get": {
"description": "Returns pets based on ID",
"summary": "Find pets by ID",
"operationId": "getPetsById",
"responses": {
"200": {
"description": "pet response",
"content": {
"*/*": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
}
}
},
"default": {
"description": "error payload",
"content": {
"text/html": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
}
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"description": "ID of pet to use",
"required": true,
"schema": {
"type": "array",
"items": {
"type": "string"
}
},
"style": "simple"
}
]
}
}
To simplify and focus on the real challenge, we would need, for each defined endpoint, to be able to retrieve, and convert to OpenAPI format:
One option would be to have a script that starts Kibana, let all plugins registered their handlers, and then work from the routes that have been registered to core's httpService. This would require to start Kibana in a 'sandboxed' mode that would stop after setup, and collect the routes from core, and then work from there.
These would be directly accessible from the route's config
We would have access to body, params and query's validation schemas. We would 'just' have to be able to interpret and convert them to OA format at runtime. Of course, this is not something that is currently doable, and would requires to expose metadata on all of kbn/config-schema types to be able to perform reflect-ish operations on the schema.
I don't think there is any way to retrieve this information using this approach.
The second option would be to use AST to parse the code, find all calls to Router#get|post|XXX, and work from there (which is already some heavy parsing work tbh). If it's probably more powerful, this also sounds like colossal work to handle.
We should be able to parse the route's configuration and retrieve these infos from there. Note that we would need to be able to resolve variables to scenarios such as path: prefix + "/endpoint".
We should be able to retrieve the validation schema nodes, and then use typeChecker.getTypeAtLocation to retrieve the schema type. From there, we would need to retrieve the schema's result type, and then convert it to OpenAPI format.
We should be able to find calls to res.ok and use typeChecker.getTypeAtLocation to infer the returned type. We would then need to convert the TS type to openAPI format. I see that https://github.com/grantila/ts-to-openapi exists, so it should probably be doable ourselves (or finding a more used/adopted lib)
This is what is done by https://www.npmjs.com/package/express-openapi for example
module.exports.get.apiDoc = {
description: 'A description for retrieving a user.',
tags: ['users'],
operationId: 'getUser',
// parameters for this operation
parameters: [
{
in: 'query',
name: 'firstName',
type: 'string'
}
],
responses: {
default: {
$ref: '#/definitions/Error'
}
}
};
Not sure this has real value compared to just have solutions teams manually maintain their openAPI spec outside of the raw code.
Pinging @elastic/kibana-platform (Team:Platform)
Just brainstorming here, haven't given this a lot of thought, but could we generate source code from the OpenAPI spec instead of generating OpenAPI spec from the source code?
If you "compile" the spec it would generate a my_openapi_routes.ts file which exposes a method like mountOpenApiRoutes(router: IRouter, routeHandlers: MyRouteHandlers). The MyRouteHandlers type will be defined inside my_openapi_routes.ts and will ensure that a route handler is defined for every route and method defined by the spec. We could also generate types from the json schema so that the route handlers have typed parameters and expected return types for the responses.
Would need to think more about it, but this seems to be a viable option yes. Seems technically way easier (not easy, easier) than the other way around.
I think in general it is preferable to keep OpenAPI definitions as your source of truth. This makes the solution language agnostic and allows to generate implementations in multiple languages.
There are projects like this though - https://tsoa-community.github.io/docs/getting-started.html#defining-our-first-model
Java has a similar set of libs to annotate your controllers, but Java has more support for reflection to process method parameters, return types, etc.
I think deciding the source of truth and direction is as much a team/workflow decision as a technical one. That said, my preference is to use JSON Schema & OpenAPI to spec shape & behavior and then generate TS types, JS clients, and servers from that.
What @rudolf describes is essentially the plan for Ingest Manager but ours is currently more manual/gradual
Ingest Manager OpenAPI overview
$refs for better tooling supportnpx swagger-cli bundle -o bundled.json -t json entrypoint.yamlnpx openapi-typescript bundled.json --output schema.tsnpx swagger-typescript-api -p bundled.json -n client.tspath and method post are added manually, but it'd be trivial to find that info for a specific schema via the operationId (e.g. post-fleet-agents-enroll) or loop through all entriesThere are many options for generating clients. Many (most?) allow you to choose a transport (nodejs http, fetch, axios, etc) but no customization in the client code that's generated. We'll want one which allows us to supply our own templates so we can use core.http or anything else we want to add. OpenAPI Generator is probably the most popular & well-supported. swagger-typescript-api also looks pretty nice (it's templates are at https://github.com/acacode/swagger-typescript-api/tree/master/src/templates but you can provide your own)
This is great -- thanks for detailing this out @pgayvallet! Just want to share this issue we created over on the Security Detections side that captures what an initial PoC could look like from our perspective: https://github.com/elastic/kibana/issues/81964
There are many options for generating clients. Many (most?) allow you to choose a transport (nodejs
http,fetch,axios, etc) but no customization in the client code that's generated. We'll want one which allows us to supply our own templates so we can usecore.httpor anything else we want to add. OpenAPI Generator is probably the most popular & well-supported.swagger-typescript-apialso looks pretty nice (it's templates are at https://github.com/acacode/swagger-typescript-api/tree/master/src/templates but you can provide your own)
I was mostly working with Java/Maven generator, but in any case supplying your own templates is pretty easy, main concern is who would maintain those templates.
Most helpful comment
This is great -- thanks for detailing this out @pgayvallet! Just want to share this issue we created over on the Security Detections side that captures what an initial PoC could look like from our perspective: https://github.com/elastic/kibana/issues/81964