spring-hateoas icon indicating copy to clipboard operation
spring-hateoas copied to clipboard

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

Open roncsak opened this issue 1 year ago • 3 comments

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
}

roncsak avatar Oct 29 '23 09:10 roncsak

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?

odrotbohm avatar Nov 08 '23 14:11 odrotbohm

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?

roncsak avatar Nov 16 '23 16:11 roncsak

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 :)

serpro69 avatar Jun 25 '24 19:06 serpro69