spring-cloud / spring-cloud-contract

Support for Consumer Driven Contracts in Spring
https://cloud.spring.io/spring-cloud-contract
Apache License 2.0
718 stars 439 forks source link

Improve REST Docs documentation generation #1084

Open wkorando opened 5 years ago

wkorando commented 5 years ago

Spring Cloud Contract is able to generate REST Docs from contracts, but currently the generated docs are quite spare, missing important information such as the ability to provide descriptions for request and response fields. While markup is half the battle, for providing this information in the contracts it could work similar to how matches are used, YAML example below:

name: add-produce
description: Operation for adding a new produce item
request:
  method: POST
  url: /api/v1/produce
  headers:
    Content-Type: application/json;charset=UTF-8
  body:
    name: "Kiwi"
    subName: ""
    quantity: 75
  matchers:
    headers:
    - key: Content-Type
      regex: "application/json.*"
    body:
      - path: $.quantity
        type: by_regex
        value: "[0-9]+"
      - path: $.name
        type: by_regex
        predefined: only_alpha_unicode
  descriptions:
    headers:
    - key: Content-Type
      description: "Produce endpoint expects JSON encoded request bodies"
    body:
      - path: $.name
        description: "Name of new produce item"
      - path: $.subName
        description: "Sub-name of new produce item providing a more specific name for the produce item"
      - path: $.quantity
        description: "Number of produce item currently in stock"
response:
  status: 200
  body:
    id: 10
    name: "Kiwi"
    subName: ""
    quantity: 75
  headers:
    Content-Type: application/json;charset=UTF-8
  matchers:
    body:
      - path: $.id
        type: by_regex
        value: "[0-9]+"
  descriptions:
    headers:
    - key: Content-Type
      description: "Produce endpoint provides responses in JSON"
    body:
      - path: $.id
        description: "Unique id of new produce item"
      - path: $.name
        description: "Name of new produce item"
      - path: $.subName
        description: "Sub-name of new produce item providing a more specific name for the produce item"
      - path: $.quantity
        description: "Number of produce item currently in stock"
artemptushkin commented 5 years ago

It is an important feature-request and required feature for SCC. I'd like to add my groovy contract example and an architecture vision. It is pretty the same with @wkorando one

Groovy contract:

Contracts.make {
    description {
        """
This is example contract description of REST API with declared documentation to generate .adoc file
"""
    }
    request {
        method 'GET'
        url '/some/api'
        headers {
            header("someHeader", /* value = */ "123")
        }
        body (
            exampleField: 100,
            anotherField: 50
        )
        documentation {
            headers {
                header("someHeader", /* description = */ "Some specific header")
            }
            bodyFields {
                fieldPath('exampleField').type(String.class).description("Very important field")
                fieldPath('anotherField').type(String.class).description("Another field")
            }
        }
    }
    response {
        status 200

        body (
            responseField: "foo",
            wowField: "baz"
        )

        documentation {
            bodyFields {
                fieldPath('responseField').type(String.class).description("Response field description")
                fieldPath('wowField').type(String.class).description("It is wow field")
            }
        }
    }
}

I think that it can be implemented by generating .adoc files on the same with as me already generate tests and stubs.

Example producer/publisher workaround:

  1. gradle task 'generateAsciiDoc' -> result: ...build/stubs/META-INF/groupId/artifactId/version/aciidoctor/some-contract-name.adoc

We can publish it to remote GIT repo or stub jar.

A producer can fetch it from the remote repo or just take from the classpath test dir adoc and put it to ... here is a tricky part. I think about output directory of restdocs generation process

Example producer workaround:

  1. gradle copyContracts -> result: contracts, stubs and adoc inside the build/stubs dir ->
  2. gradle test -> result: adocs copied to outputdirectory of org.springframework.restdocs.JUnitRestDocumentation the same as output directory of @AutoConfigureRestDocs

At this case, a producer will be available to use a custom include to include generated adoc components to the result html file.

@marcingrzejszczak @OlgaMaciaszek please, share feedback about the vision above

OlgaMaciaszek commented 5 years ago

@wkorando @artembilan This is a good idea. I like adding documentation section in the contracts, but not sure about the chained methods; I think we should try the map approach like with body or passing it in arguments, like with bodyMatchers section method to be more consistent (@marcingrzejszczak wdyt?).

I'm not sure about what you are writing regarding the client? Usually, it will be the producer (or server) and not the client that will want to publish docs on their side.

artemptushkin commented 5 years ago

By client, I mean producer, yes. But there are cases when producer fetches contracts from a remote repo and not keeps at the classpath, at this case he is a client of it :)

I updated the message above

OlgaMaciaszek commented 5 years ago

Ok, looks good to me; @marcingrzejszczak wdyt?

marcingrzejszczak commented 5 years ago

I think the map notation would be more consistent with what we would have in yaml. I think that now when we're building api we should start with yaml since it's completely static and thebln build a dsl around it in java, groovy and kotlin.

Btw we'll have to add the missing matchers sections in the core java contract code. Right now we're doing some dsl property magic to make this work.

marcingrzejszczak commented 4 years ago

A prerequisite for this is (https://github.com/spring-cloud/spring-cloud-contract/pull/1466 and #1465 )

marcingrzejszczak commented 4 years ago

Also for YAML we need to wait for this https://github.com/spring-projects/spring-restdocs/issues/248