stoplightio / prism

Turn any OpenAPI2/3 and Postman Collection file into an API server with mocking, transformations and validations.
https://stoplight.io/open-source/prism
Apache License 2.0
4.2k stars 343 forks source link

Preferred response based on request body key #1838

Closed hjblom closed 2 years ago

hjblom commented 3 years ago

User story.

As a developer, I can do requests to a prism mock server, so that I can test my integration from the client side.

Is your feature request related to a problem?

Currently it is only possible to select the preffered response by providing the following header:

Prefer: "example=someExample"

For my use case, I follow the gateway pattern. This means I have 3 components: client, gateway, mock server.

(Client) -- calls gateway -> (Gateway) -- calls mock --> (Mock)

I have control over the client, but not the gateway.

In order to get specific responses from the mock, the gateway would need to be aware of specific elements in the body of the request, to add a header so that a specific response is triggered from the mock. Since this is out of my control, I would need a different mechanism to get a preffered response.

For example: If a bank card starting with 4000 is detected in a specific field in the body of the request , I would want the mock to return a specific example that works with that card number.

Describe the solution you'd like

Similar to how faker keys can be injected into the openapi specification, I would like to be able to specify a specific field, e.g. x-example-field-key, that will be used as my example key to do the example lookup.

For example:

paths:
  /users:
    post:
      summary: Creates a new user.
      consumes:
        - application/json
      parameters:
        - in: body
          name: user
          description: The user to create.
          schema:
            type: object
            required:
              - userName
            properties:
              userName:
                type: string
              firstName:
                type: string
              lastName:
                type: string
      x-example-field-key: "userName"
      examples:
        John:
          value:
            id: bda7aa1f-4dca-49cc-aada-69cac3891301
        Sarah:
          value:
            id: 017d9506-5fc3-464e-928f-b77a078cf316
      responses:
        200:
          description: Created
          content:
              application/json:
                schema:
                  type: object
                  properties:
                    id:
                      type: string                    
                      format: uuid
                      description: "UUID of created user"

Given the specification above, I would like to get the example of John for a request with the userName field key (provided by x-example-field-key) set to John and similarly for Sarah.

philsturgeon commented 2 years ago

Thanks for this use case, that’s an interesting one! We’ve had a few thoughts around custom mocking through middlewares or DSL’s before, but generally we’ve been unable to work up anything generic and useful enough, that wouldn’t just lead down the road of building a mock API that’s so full of logic it needs tests and eventually ends up in production.

435 #1791

I do like your suggested implementation, I think it’s very simple and possibly elegant.

@marbemac i know you’ve had various ideas on mock pairing and various types of customisation over the years, what do you think? ☝🏻

philsturgeon commented 2 years ago

1212

There's the idea of example pairs which we've discussed internally that only supports looking at URL parameters so that /foo/123 would pick an example called "/foo/123" from within the corresponding content type but that is an awkward convention to force on prism users who we also expect to be using Stoplight Docs as it will render those paths as a human readable name and... well that's not great.

If we do use this convention we would have to consider adding Summary and Description support for examples (https://github.com/stoplightio/studio/issues/508) so that we can hide this convention from users who happen to use Studio, making nice looking docs regardless of what docs tool people use (Swagger, ReDoc, Elements) yet still keep example pairs for Prism users.

I think example pairs could be a feature to get us started solving some of these "make prism examples more useful" user requests that keep popping up, without needing to go full persistence (#962) or custom DSL just yet.

what do you think @ryotrellim? Many use cases would be improved by having a way to return a particular example that’s not just based on the URL as has been suggested before.

hjblom commented 2 years ago

Hi @philsturgeon! This is great news!

Here are 2 possibilities to implement an accessor. Personally I prefer dot notation, but use whatever works best for prism.

I also just want to clarify that the x-example-field-key key would only be in the openapi spec and not contained within the request. It might be a future feature to include, to be able to route if it was set in the request body. But I would not add that scope to this issue.

Please let me know if I can help with testing once an implementation is available.

chohmann commented 2 years ago

@hjblom after reconsideration we do not feel that this functionality belongs in Prism. As for your gateway stripping the prefer header, you can also use the __example query parameter as documented here. Would that work for you?

We also feel that this logic of which example to request and when can be added to your test (or wherever you're calling this from) and then generate the appropriate query parameter.

We're going to close this for now.

stirredo commented 1 year ago

Is Prism mock server meant to be used for developing frontend without developing API first?

How is one supposed to use Prism for developing frontend? One could have used specific request body for designing frontend for edge cases. Providing specific response through header key limits prism to just CLI usage, which IMO, is pretty much useless.

Please reconsider this feature.

gstabel commented 1 year ago

Hi everyone. We are a mock server during front-end development today, back-end services are defined with OpenAPI 3.0, and mocked using Prism.

We want to implement a simpler version of was described on this thread...

Basically we want to use 'examples' names from the request, and (if it is a perfect match) to pair/reply with an example with the same name on the response's 'examples'. In other words, the example name becames the correlation key.

I was thinking about creating a custom version for us with these behavior... but with some guidance of the community maybe this can become an optional CLI feature/parameter.

Anyone can point us to right place to do this?

Thanks in advance!

chohmann commented 1 year ago

Hi @gstabel! We're not 100% sure we understand what you're suggesting.

Here's a couple of follow up questions to help us understand:

  1. could you explain what the difference is between how our prefer header works today and what you're describing?
  2. could you provide an example spec and the full request and response you would send/receive from Prism that illustrates your proposal?
gstabel commented 1 year ago

Hi @chohmann, thank you for your time. Let me clarify our scenario.

  1. The backend team creates the API specification and deploys the Prism mock in a development server. Then our frontend developers use Prefer header in their local environment pointing to the development mock server. For example: curl -H "Prefer: example=DOG" .... This is working great for us! So the front devs can switch between examples created by the backend team, and simulate them locally.

  2. The next step we want to achieve is to deploy all front-end features in a testing server pointing to a testing back-end mock server. This will allow our QA team to test the front-end isolated, with mocked data. Now comes the problem and idea to solve it... when everything is deployed, we can't add/change the Prefer header to change Prism's responses. We get one single response per endpoint.

If we could pair up the examples in request-response by their names... we could create a simple "request matcher" to produce different mock responses depending on the request. Only using examples.

For example, in the specification below... A request to the endpoint with name: Garfield as payload, would produce a 200 code with sound: Meow as the body. On the other hand, a name: Snoopie request, would produce a 400 code error: Silence body.

  requestBody:
    content:
      application/json:
        schema:
          type: object
          properties:
            name:
              type: string
        examples:
          CAT:
            value:
              name: Garfield
          DOG:
            value:
              name: Snoopie  
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  sound:
                    type: string
              examples:
                CAT:
                  value:
                    sound: Meow
        '400':
          description: Error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
              examples:
                DOG:
                  value:
                    error: Silence 
chohmann commented 1 year ago

@gstabel Thanks for the clarification!

Although that's an interesting approach and one that might fit your specific needs, we don't feel like this can be holistically implemented to solve every use case or a good fit for Prism's overall mission.

If you want this functionality, we think a good approach to this would be to implement a proxy in between your test environment's frontend and your test environment's Prism server that performs this matching logic between the request body and the preferred response example and sends the prefer header to prism for the example you want returned.

chohmann commented 1 year ago

@gstabel to further illustrate our suggestion of putting a proxy between your client and Prism that houses your custom response choosing logic, we've created this ExampleChooserPrismProxy repo that you can follow.

nicholasirsyad commented 12 months ago

Hi all,

I don't see why it's so hard to implement choosing to return a certain response based on what the users have provided as example pairs.

When the user (mock creator) provides, for example: Pair 1: request body: { attr1: val1, attr2: val2 }

reponse: { result1: "result1" }

Pair 2: request body: { attr1: val2.1, attr2: val2.2 }

response { result1: "result2" }

Then why not simply store a Map containing the hashed stringified json of the request body as key, and return the appropriate response body?

Perhaps I'm missing something - if someone can enlighten me on the issue I'd appreciate it.