So I'm no swagger expert, but all systems using swagger require you to have the swagger specification in JSON or YAML defining all endpoints (and such) of your API.
My question is: Are there know ways to generate these specification files based on the actual source code?
I'm asking, because it seems very hard to keep endpoint code & documentation in sync when you start adding properties or returning slightly different results.
So when I have this code (using http4s & RhoService):
object HelloWorld {
val service = new RhoService {
GET / "hello" / 'name |>> { name: String =>
Ok(Json.obj("message" -> Json.fromString(s"Hello, ${name}")))
}
}
}
It would be great if it could produce (in some way:)
/hello/{name}:
get:
tags:
- "HelloWorld"
summary: ""
description: ""
operationId: "getHellobyName"
produces:
- "application/json"
parameters:
- name: "name"
in: "path"
description: ""
required: true
type: "string"
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/Hello"
security:
- api_key: []
Disclaimer: I'm the author of tapir.
rho is one possibility. Another approach is to completely separate the description of the API endpoint from the business logic.
Having a description of an endpoint, which is a regular Scala value, it can be then interpreted as either a server (given the "business logic" function), or documentation.
Two Scala libraries that can provide both http4s and OpenAPI interpreters are tapir and typeapi.
It is not documented well, but apparently http4s' RhoService adds middleware to generate a swagger.json based on your routes:
Fetch it by calling 'http://localhost:8080/swagger.json'
Git source: https://github.com/http4s/rho/blob/0c5aed48aeeea18b1d66d88b58cd3deea733f070/swagger/src/main/scala/org/http4s/rho/swagger/SwaggerSupport.scala#L30
Disclaimer: I’m the author of endpoints4s.
Similar to tapir (mentioned in another answer) endpoints4s is a library that can produce http4s servers and OpenAPI documentation for HTTP endpoints.
You would write your example like the following:
// --- This is the description of your HTTP service
import endpoints4s.algebra
case class Hello(message: String)
trait HelloWorldEndpoints
extends algebra.Endpoints
with algebra.JsonEntitiesFromSchemas {
implicit val helloJsonSchema: JsonSchema[Hello] =
field[String]("message")
.xmap(message => Hello(message))(hello => hello.message)
val hello: Endpoint[String, Hello] =
endpoint(
get(path / "hello" / segment[String]("name")),
ok(jsonResponse[Hello])
)
}
// --- This is an OpenApi documentation of the endpoints defined
// --- in HelloWorldEndpoints
import endpoints4s.openapi
import endpoints4s.openapi.model.{ Info, OpenApi }
object HelloWorldDocumentation
extends HelloWorldEndpoints
with openapi.Endpoints
with openapi.JsonEntitiesFromSchemas {
val api: OpenApi =
openApi(Info(title = "Hello API", version = "1.0"))(
hello
)
}
// --- This is an http4s server that implements the endpoints defined
// --- in HelloWorldEndpoints
import endpoints4s.http4s
import cats.effect.IO
import org.http4s.HttpRoutes
object HelloWorld
extends http4s.server.Endpoints[IO]
with http4s.server.JsonEntitiesFromSchemas
with HelloWorldEndpoints {
val service: HttpRoutes[IO] = HttpRoutes.of(
routesFromEndpoints(
hello.implementedBy(name => Hello(s"Hello, ${name}"))
)
)
}
// --- And this is an http4s service that publishes the OpenApi documentation
object OpenApiServer
extends http4s.server.Endpoints[IO]
with http4s.server.JsonEntitiesFromEncodersAndDecoders {
val openApiService: HttpRoutes[IO] = HttpRoutes.of(
routesFromEndpoints(
endpoint(
get(path / "open-api.json"),
ok(jsonResponse[OpenApi])
).implementedBy(_ => HelloWorldDocumentation.api)
)
)
}
Related
I'm trying to implement the #auth directive in GraphQL for use with Neo4j as documented here:
https://neo4j.com/docs/graphql-manual/current/auth/auth-directive/
With a jwt token that is taken from firebase, and should have all of the necessary fields, including admin roles
The problem is that whenever I try to use one of the generated queries with the admin bearer token it says "forbidden" when the auth directive is attached to it.
The full discussion of this issue between me and ChatGPT, which includes extensive trial and error that was done before writing this question, logs, code snippets etc, can be found here for reference:
https://firebasestorage.googleapis.com/v0/b/awtsmoos.appspot.com/o/ai%2FBH_1674546675630.html?alt=media&token=17b653c4-5db2-4bca-8bc1-3cc3625e5a6c
Just to summarize some key code parts, I'm trying to follow the setup example like this essentially:
const neoSch = new graphqlNeo.Neo4jGraphQL({
typeDefs: typeDefs,
driver:dr,
resolvers:rez,
plugins: {
auth: new neoGraphQLAuth
.Neo4jGraphQLAuthJWTPlugin({
secret:
secret.private_key.toString()
})
}
})
Here's the schema that it suggested after I gave it some samples, doesn't work:
type Homework {
id: ID #id
title: String!
steps: [Step!]! #relationship(
type: "HAS",
direction: OUT
)
}
extend type Homework #auth(
rules:[
{
allow: CREATE,
roles:[
"ADMIN"
]
}
]
)
extend type Homework #auth(
rules:[
{
allow: READ,
}
]
)
The token itself is getting properly passed in, as discussed at length in the ChatGPT session, I'm not sure what else it is?
Where the secret property is my JSON of a service account taken from firebase, then in my Apollo context I've tried lots of trial and error with no success, in this matter, but here was one implantation I tried:
app.use(
"/etsem",
cors(),
bodyp.json(),
exp4.expressMiddleware(
serv, {
context: async info => {
var rz = {
req:info.req,
headers:info.req.headers
}
return rz;
}
}
)
)
But still I got the forbidden error
I'm trying to build a dart package which generates API source code from OData JSON representation file. I tried using source_gen, but it appears to generate code based on annotations or existing source code (e.g. JSON serializer generated code for existing class).
Goal: Generate multiple dart codes based on JSON data. E.g.: Say my JSON is:
{
"user": {
"income": "Decimal", //Decimal is the type, which I translate it into dart type
}
}
and my generated code would be:
user.dart:
import "package:decimal/decimal.dart";
class User {
final Decimal age;
User(this.age);
}
What are you trying to do is already implemented, when it comes to API description.
You can generate code with classes from .proto or swagger .yaml/ .json files.
Proto file generation for gRPC API
Proto file example:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
Swagger rest api code generation from .yml/json files
Look:
Having a hard time trying to figure out the "right" way of doing this. I'm using Micronaut to create a REST service that uses OpenAPI/Swagger by transforming my API annotations into open API specs. I'm trying to eliminate annotation duplication between APIs that take the same parameters in.
#Operation(
operationId = "...",
summary = "..",
description = "...",
)
#Post(uri = "something/{object_type}")
fun apiA(#Parameter(name = "", description = "") object_type: String) {
}
#Post(uri = "something-else/{object_type}")
// This should have the same #Parameter as the above api but I don't want to copy/paste
fun apiB(object_type: String) {}
I've tried to create my own annotation that has #Parameter on it but it doesn't seem to inherit. I know OpenAPI has a "component" concept but I'm not sure where my particular framework wants me to define components. Any pointers would be greatly appreciated.
I have a java spring boot service mesh of services.
I am using open api for swagger documentation.
When I run my spring cloud gateway and access http://localhost:{my-port}/swagger-ui.html, it works fine and loads the swagger ui which mentions /v3/api-docs in the "Explore" title bar.
Also, when I add the service name to the url like so /v3/api-docs/my-service, it works fine.
However, I want to try a list of dropdown services instead of manually appending the service name for each service. I have tried this grouping code in my gateway:
#Bean
fun apis(): List<GroupedOpenApi> {
val groups = ArrayList<GroupedOpenApi>()
val definitions = locator!!.routeDefinitions.collectList().block()
definitions!!.stream().filter { routeDefinition -> routeDefinition.id.matches(".*-service".toRegex()) }.forEach { routeDefinition ->
val name = routeDefinition.id.replace("-service".toRegex(), "")
GroupedOpenApi.builder().pathsToMatch("/$name/**").setGroup(name).build()
}
return groups
}
Now, after this when I access http://localhost:{my-port}/swagger-ui.html, it shows a drop down list of all the service with the "-service" removed. But after selection, the docs don't show up.
Although, if I access it in a separate tab with http://localhost:{my-port}/v3/api-docs/my-service, it shows me the json of all the paths and works fine. Just doesn't load in the swagger ui.
Here's gateway route for one of the service and openapi:
- id: my-service
uri: lb://my-service
predicates:
- Path=/api/v1/my/**
filters:
- name: CircuitBreaker
args:
name: my-service
fallbackuri: forward:/myServiceFallBack
I used Swagger Yaml to describe an endpoint and generate the mock server. The existing endpoint (that I'm mocking) doesn't follow RESTful principles 100%, so I simply want to overwrite the response that is returned by the mock server. The simple server code is shown below:
var swagger = require('swagger-server');
var server = swagger('map-cache.yaml');
var port = 7072;
server.post('/map-qa_trunk/v2/getData', function(req, res, next) {
var foo = {
err : 123,
msg : "error message"
};
res.json(foo);
});
server.listen(port, function() {
console.log('Map Cache Mock Server is now running at http://localhost:' + port);
});
In the Yaml definition, there is an object defined called MapResponseData, how do I create an instance of this object so that I can populate it as needed and return in the res.json()? Something similar to below:
var response = getMapResponseData(); // don't know what this call should be
response.fieldA = 123;
res.json(response);
I am guessing this should be possible, since Swagger parsed the YAML file and is aware of all definitions that were specified.
Try outputting the request object to console.log to see if you can find reference to the swagger definition. Another option would be to pull the parsed swagger definition from the yaml file (using js-yaml for example) and extracting from there.
However, my best advice is to use swagger-tools instead of swagger-server. The swagger-server package is alpha version and has fewer downloads, revisions, and users than swagger-tools. Benefit of swagger-tools is that it will be actively maintained and there is a larger community that can support you. To convert your project to swagger-tools, use swagger.io > Swagger Editor > Online Editor > Paste yaml in left pane > Generate Server > Node.js
In swagger-tools the entire Swagger Yaml definition is contained in each request object:
req.swagger.swaggerObject
and you can pull the response object definitions from that as needed.