Openapi-specification: Using Links to set a field in the target operation's request body

Created on 31 May 2018  Â·  7Comments  Â·  Source: OAI/OpenAPI-Specification

Hi,

I would like to use links to set certain fields in the request body of another operation. For example using the petstore example, I would like to create a pet and grab the pet id from the response. Then I would like that pet id to be reflected in the request body for the "PUT" pet method to update the pet.

I've seen examples of links where they take the value from the response and put it into another request's header, query, path parameters. However I am not sure how to use the setting values in the requestBody correctly. On the links/requestBody documentation website there is an example but it sets the request body like this, "requestBody: '$response.body#/id'". From my understanding, this would make the entire requestBody the id in plaintext instead of a xml or json object.

How would I set the requestBody's id to "$response.body#/id"?

I also see on the swagger.io website saying that we can prefix parameters.

If two or more parameters have the same name, prefix the names with the parameter location – path, query, header or cookie, like so:
parameters:
path.id: ...
query.id: ...

Would it be possible to do prefix with the requestBody like this:
parameters:
requestBody.id: '$response.body#/id'

I made a .yml file in case you wanted to see what I am trying to achieve. You can get the link to the raw file and explore it on the petstore swagger

Links:
Sample links .yml file: https://github.com/jonathann92/PlainFiles/blob/master/openapi.yml#L174
Petstore swagger: http://petstore.swagger.io/
Documentation for links: https://swagger.io/docs/specification/links/#requestBody

links

Most helpful comment

Any update on this? I'm also in need of the use case explained by @jonathann92. Currently, links are not usuable for operations with several properties in the target request body. It feels like the most common use case in this scenario is setting just certain properties in the target request body.

I don't understand what the purpose of using a link to a single property to set an entire request body is. If your request body only has one property in it, then why would it be a request body and not a parameter in the first place?

I get that you can instead link an entire response to an entire request body, but it's REALLY hard to design an API in a symmetric way like this. More often, you have some properties in one operation's response that correspond to some properties in another operation's request body. The current "all or nothing" support for linking to request bodies seems rather obscure and impractical.

It would be much more useful to follow the same structure of the existing implementation of parameters links, for setting only certain properties in a request body. I think there's a simpler way to do it rather than the suggestion made by @darrelmiller. Like this:

links:
  getInventory:
    operationId: getInventory
    requestBody:
      id: '$response.body#/id'

The last line maps the property id (in the target request body) to the property id (from the source response). This way, the key is used in the exact same way it's currently used for the parameters keyword.

Is there a reason it wasn't implemented this way originally? This standardizes the syntax for parameters and requestBody keywords. Setting only certain fields in a target request body is the common use case. Setting an entire request body is the edge case. The implementation and syntax should cater to the former.

All 7 comments

This currently isn't possible. Link Parameter keys are just parameter name references, not expressions. This is an interesting enhancement. I'm not sure exactly how many people would use it, nor what the challenges are to implement it.

I think that maybe the right way to implement this, would be to add a new parameter type that could use expressions as a target.

openapi: 3.0.0
info: 
  title: Experiment
  version: 1.0
paths:
  '/':
     get:
       responses:
         '200':
            description: ok
            links:
              submitLink:
                operationId: postSubmit
                parameters:
                  foo: request.body.foo
  'submit':
    post:
      operationId: postSubmit
      parameters:
        - name:  foo
          in: expression
          target: request.body.foo

Although, having said, this. I'm really not sure how a client would construct the rest of the requestBody. Feels a bit weird to me.

thanks for replying quickly! I just checked my .yml sample and I forgot I removed the update pet section. I added the update pet api endpoint in the .yml file.

I find if we can take data coming back from responses like a pet id and pet category id would be helpful to make workflows for an api. Maybe we can format it something like in my example file. We would assign the schema property to the value in the response.body.id and then the rest of the schema stays as their default values

https://github.com/jonathann92/PlainFiles/blob/25783adb7b4c866877c96c575a7627a774dc5f71/openapi.yml#L205

Any update on this? I'm also in need of the use case explained by @jonathann92. Currently, links are not usuable for operations with several properties in the target request body. It feels like the most common use case in this scenario is setting just certain properties in the target request body.

I don't understand what the purpose of using a link to a single property to set an entire request body is. If your request body only has one property in it, then why would it be a request body and not a parameter in the first place?

I get that you can instead link an entire response to an entire request body, but it's REALLY hard to design an API in a symmetric way like this. More often, you have some properties in one operation's response that correspond to some properties in another operation's request body. The current "all or nothing" support for linking to request bodies seems rather obscure and impractical.

It would be much more useful to follow the same structure of the existing implementation of parameters links, for setting only certain properties in a request body. I think there's a simpler way to do it rather than the suggestion made by @darrelmiller. Like this:

links:
  getInventory:
    operationId: getInventory
    requestBody:
      id: '$response.body#/id'

The last line maps the property id (in the target request body) to the property id (from the source response). This way, the key is used in the exact same way it's currently used for the parameters keyword.

Is there a reason it wasn't implemented this way originally? This standardizes the syntax for parameters and requestBody keywords. Setting only certain fields in a target request body is the common use case. Setting an entire request body is the edge case. The implementation and syntax should cater to the former.

@darrelmiller any update on this topic? (see :point_up: )

I agree with others above, the current requestBody option of Link object is not useful in most cases.

Thinking this through...

The requestBody schema of the link target may be an object or a list. So we need to use a JSON Pointer to address the linked fields in it. A JSON Pointer can address into nested objects, or "first item of a list" etc, all potentially necessary here. A runtime-expression provides this feature, via the fragment portion.

So I think @darrelmiller's proposal is the best idea:

      parameters:
        - name: foo
          in: expression
          target: $request.body#/foo

But not all runtime-expressions would be valid, since we can only target elements of the request, so an expression like $response.body#/status would be meaningless in this context. Since we already have good options for parameters in other parts of the request, maybe we actually just want this:

      parameters:
        - name: foo
          in: body
          target: /foo

Now the target is just a bare JSON Pointer, rather than a runtime-expression.

One other thing we need to consider is what to do about the style field of the Parameter object. It seems like we should just omit it, along with schema, explode and allowReserved since they are not relevant - we want to use the schema and serialization style already defined for the request body. It should be invalid to specify any of these fields in conjunction with in: body.

So a full example would look like:

openapi: 3.0.0
info: 
  title: Experiment
  version: 1.0
paths:
  '/':
     get:
       responses:
         '200':
            description: ok
            links:
              submitLink:
                operationId: postSubmit
                parameters:
                  foo_param: $response.body#/foo_val
  '/submit':
    post:
      operationId: postSubmit
      parameters:
        - name: foo_param
          in: body
          target: /foo

Meaning:
_"take the foo_val value from GET / [200] response and use it as foo in request body to POST /submit"_

It does not matter if the postSubmit request schema has other required fields which we don't specify as parameters, it just means that they cannot be sourced from the linked request. The client may ask a user to fill them out manually, or in my use case (automated testing) I will generate fake data that matches the schema for those, while extracting foo_val from the previous request.

I can think of one other alternative construction, which doesn't use the parameters section and is defined within the Link object only. I'll probably be using this in the interim, since I can define it as an extension property:

openapi: 3.0.0
info: 
  title: Experiment
  version: 1.0
paths:
  '/':
     get:
       responses:
         '200':
            description: ok
            links:
              submitLink:
                operationId: postSubmit
                x-requestBodyParameters:
                  /foo: $response.body#/foo
  '/submit':
    post:
      operationId: postSubmit

Where the key /foo is a JSON Pointer into the requestBody of the link target, and the value $response.body#/foo is a runtime-expression to extract a value from the current operation.

One advantage of this link-driven format is if you are linking to an operation in a schema that you don't control. The author of the other schema may not be aware of which body fields need to be exposed as parameters in order for you to make a successful link, but it doesn't matter here as we can specify everything we need to as the link author.

I've recently encountered this same problem. Based on the above discussion and my own experience, I think this is not an uncommon use case for links and we should find some way to broaden the definition of links to support it.

FWIW I favour the x-requestBodyParameters form described above, defined as part of Link object, as this fits naturally when defining the equivalent for "backlinks" as discussed in #2196

Otherwise people might wonder what is the point of defining request body fields again as parameters since it would seem obvious that all fields in the request body schema are already a type of parameter.

So the use-case for this feature is specifically around defining Links, how to specify that values from the response of one operation should be used in the request body of a downstream operation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nelz9999 picture nelz9999  Â·  4Comments

Prasanthmv picture Prasanthmv  Â·  4Comments

jblazek picture jblazek  Â·  3Comments

ricellis picture ricellis  Â·  3Comments

kolisko picture kolisko  Â·  4Comments