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

Prevent or customize Link objects of models and collections for better API documentation #2037

Open
roncsak opened this issue Oct 29, 2023 · 3 comments

Comments

@roncsak
Copy link

roncsak commented Oct 29, 2023

Hello there,
I'd like to document my API project and I am using org.springdoc:springdoc-openapi-starter-common and org.springdoc:springdoc-openapi-starter-webmvc-ui for doing so.
All the schemas in the generated OpenAPI 3.0.1 documentation, includes the schema called Links which follows:

"Link": {
  "type": "object",
  "properties": {
    "href": {
      "type": "string"
    },
    "hreflang": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "type": {
      "type": "string"
    },
    "deprecation": {
      "type": "string"
    },
    "profile": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "templated": {
      "type": "boolean"
    }
  }
}

Therefore it is visible in the Swagger UI, as well:
image

My concern with this is that I, as an API provider I don't want to create false hopes in my API consumers that for Links, all the properties (hreflang, title, etc) will be available. Based on this draft, href is the only REQUIRED property, the rest are OPTIONAL.

The thing is, that properties of a given Schema are generated based on the properties of the RepresentationModel (like below), but Link schema generation won't follows this approach. So it would be good, if properties of Link would be generated to the schema based on actual Link properties (or Annotations?) somehow.

@Entity
@Table(name = "customers")
public class Customer extends RepresentationModel<Customer> {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "customerId", nullable = false)
    private Long id;

    @Column(name = "name", nullable = false)
    @Schema(type = "string", example = "My fine ass Company")
    public String name;

//  ... constructors, getters and setters ... 

Also, that would mean, that I might not want a dedicated Link schema because for my Customer object I would like to have href and name properties but for a different object I might want only href.

Connected to this topic, once the definition of custom Links becomes available, I'd like to have the ability to provide example for them. Currently, the only way (that I'm aware of) to do so is the following:

@Entity
@Table(name = "customers")
@Schema(example = "{\"_links\": {\"self\": {\"href\": \"http://<domain:port>/customers\"}}}")
public class Customer extends RepresentationModel<Customer> {

The problem with this approach, that it is completely overrides additional properties and annotations of the RepresentationModel and that would I couldn't rely on the autogeneration of the properties and their Annotations, regarding OpenAPI examples.
image

Summary

  • Are there any way, to override the default Link schema?
  • Is it possible to define Link schema by RepresentationModel?
  • Is it possible to define my own examples for these Links?

My ultimate goal is to provide a straighforward API documentation to my API consumers which in this context means, Swagger UI won't suggest properties of a given Schema that may never be received.

build.gradle
plugins {
  id 'java'
  id 'org.springframework.boot' version '3.1.5'
  id 'io.spring.dependency-management' version '1.1.3'
  id 'org.asciidoctor.jvm.convert' version '3.3.2'
}

group = 'hu.roncsak'
version = '0.0.1-SNAPSHOT'

java {
  sourceCompatibility = '17'
}

repositories {
  mavenCentral()
}

ext {
  set('snippetsDir', file("build/generated-snippets"))
  h2Version = '2.1.214'
  springBootStarterVersion = '3.1.5'
  springOpenApiStarterVersion = '2.2.0'
  springRestDocsMockMvcVersion = '3.0.0'
  springSecurityVersion = '6.1.5'
}

dependencies {
  implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootStarterVersion"
  implementation "org.springframework.boot:spring-boot-starter-data-rest:$springBootStarterVersion"
  implementation "org.springframework.boot:spring-boot-starter-hateoas:$springBootStarterVersion"
  implementation "org.springframework.boot:spring-boot-starter-oauth2-authorization-server:$springBootStarterVersion"
  implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootStarterVersion"
  implementation "org.springframework.boot:spring-boot-starter-security:$springBootStarterVersion"
  implementation "org.springframework.boot:spring-boot-starter-web:$springBootStarterVersion"
  implementation "org.springdoc:springdoc-openapi-starter-common:$springOpenApiStarterVersion"
  implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:$springOpenApiStarterVersion"
  developmentOnly "org.springframework.boot:spring-boot-devtools:$springBootStarterVersion"
  runtimeOnly "com.h2database:h2:$h2Version"
  testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootStarterVersion"
  testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:$springRestDocsMockMvcVersion"
  testImplementation "org.springframework.security:spring-security-test:$springSecurityVersion"
}

tasks.named('test') {
  outputs.dir snippetsDir
  useJUnitPlatform()
}

tasks.named('asciidoctor') {
  inputs.dir snippetsDir
  dependsOn test
}
@odrotbohm
Copy link
Member

I think you're barking up the wrong tree here. Whatever Springdoc does with types is not something we have control over. Have you considered bringing this up with their team?

@roncsak
Copy link
Author

roncsak commented Nov 16, 2023

Hi @odrotbohm,
sorry for the delay.

I probably get what you mention and it seems reasonable.
I fear I'm not that pro dev that I can explaing probably but probably this situation could be solved by any of the library.

What I know is that I'd like to use spring-hateoas. What I also know, that when I want to set a link to my RepresentationModel I have to use one of the add() function which expects a Link as a parameter. In order to have this, all my models have to be extended by RepresentationModel like this:

@Entity
public class Customer extends RepresentationModel<Customer> {

And having this will allow my models to have the link parameters. From springdoc side I think its completely natural that it reflects the model Link within the Schema.
I do not know what could be done from their side (but probably I will open a GH issue on their side as well based on your suggestion) if its possible at all.

However, I still think that somehow it would be good to define my own Link model what I can use for my RepresentationModels. What do you think? Shall we wait for the answer from springdoc maintainers?

@serpro69
Copy link

Don't know if I'm late to the ball, but just want to throw in my 2 cents anyways.
The described issue here is one of the reasons I don't like swagger and everything around it. Well, that, plus a ton of annotations that you need to add to each method you want to document.
Try spring-restdocs - it's a very nice concept of documenting APIs that is growing on me quite a bit. Just a tip, take it or leave it :)

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

No branches or pull requests

3 participants