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

Support structured request bodies #242

Closed
LucasPickering opened this issue May 28, 2024 · 0 comments · Fixed by #252
Closed

Support structured request bodies #242

LucasPickering opened this issue May 28, 2024 · 0 comments · Fixed by #252
Labels
qol Improvements that make usage smoother, without introducing new functionality
Milestone

Comments

@LucasPickering
Copy link
Owner

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Right now, request bodies are only defined as strings (technically you can pass any primitive value, which will parse as a template). There is a use case for defining structured bodies, where the exact structure and semantics would be determined by the Content-Type header.

See #235 for more context.

Describe the solution you'd like
A clear and concise description of what you want to happen

By default, the body is deserialized as just a template. For structured data, you have to provide a tag to tell Slumber the structure type. Some examples:

chains:
  cod_image: !file
    path: ./cod_photo.jpg
requests:
  create_fish: !request
    method: POST
    url: "{{host}}/fishes"
    headers:
      Content-Type: application/json
    body: !json
      id: "cod"
      name: "Rod"
  upload_fish_image: !request
    method: POST
    url: "{{host}}/fishes/image"
    headers:
      Content-Type: multipart/form-data
    body: !form
      fish_id: "cod"
      fish_image: "{{chains.cod_image}}"

Advantages:

  • Unambiguous and easy to deserialize
  • Checking if the body type matches the content type is easy. For each content type we can define a simple list of valid body types.

Disadvantages:

  • You have to define your content type in two places: the Content-Type header and the body tag

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered

We could support any type for the body (i.e. arbitrary primitives, maps, and arrays), but that introduces ambiguity. I considered this very early on in the project and decided against it for that reason. For example:

requests:
  create_fish: !request
    method: POST
    url: "{{host}}/fishes"
    headers:
      Content-Type: application/json
    body: '{id: "cod", name: "Rod"}'

What would the body be here? Should we submit {id: "cod", name: "Rod"} or "{id: \"cod\", name: \"Rod\"}"? The user probably meant the former but if we automatically convert the given value to JSON, then it should be stringified to the latter. In addition to being confusing and ambiguous, this would also be a massive breaking change.

Additional context
Add any other context or screenshots about the feature request here

To begin with, this should only support JSON. Forms should be added in separate PRs.

@LucasPickering LucasPickering added the qol Improvements that make usage smoother, without introducing new functionality label May 28, 2024
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates
- `Content-Type: application/json` will automatically be set for JSON bodies

The original design in the ticket was to use have the user still provide `Content-Type`, but I realized there isn't any value in that. It just requires more work for the user, and introduces another thing we need to validate (body type vs `Content-Type`). If the user needs a subset of `application/json` they can still override it. Otherwise, providing it ourselves is just more convenient.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates

The original design in the ticket was to use have the user still provide `Content-Type`, but I realized there isn't any value in that. It just requires more work for the user, and introduces another thing we need to validate (body type vs `Content-Type`). If the user needs a subset of `application/json` they can still override it. Otherwise, providing it ourselves is just more convenient.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates

An important design decision here is whether or not we should automatically set the `Content-Type` header. In the issue I wrote that we wouldn't, but during implementation I played around with both options. In the end I went with what was laid out in the issue. The pros and cons of setting it automatically:

- Convenient, reduces boilerplate

- It may be incorrect, e.g. if the user actually wants `application/geo+json`
- Implicit behavior can be confusing
- No way to un-set it (you could overwrite the header with an empty value but you can't omit it entirely)
- For more complicated types (e.g. forms), we may want to rely on the header to tells us more about the body, which is incompatible with the reverse

Another reason to leave it explicit for now is it's a non-breaking change to add the implicit behavior later.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates

An important design decision here is whether or not we should automatically set the `Content-Type` header. In the issue I wrote that we wouldn't, but during implementation I played around with both options. In the end I went with what was laid out in the issue. The pros and cons of setting it automatically:

Pros

- Convenient, reduces boilerplate

Cons

- It may be incorrect, e.g. if the user actually wants `application/geo+json`
- Implicit behavior can be confusing
- No way to un-set it (you could overwrite the header with an empty value but you can't omit it entirely)
- For more complicated types (e.g. forms), we may want to rely on the header to tells us more about the body, which is incompatible with the reverse

Another reason to leave it explicit for now is it's a non-breaking change to add the implicit behavior later.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates

An important design decision here is whether or not we should automatically set the `Content-Type` header. In the issue I wrote that we wouldn't, but during implementation I played around with both options. In the end I went with what was laid out in the issue. The pros and cons of setting it automatically:

Pros

- Convenient, reduces boilerplate

Cons

- It may be incorrect, e.g. if the user actually wants `application/geo+json`
- Implicit behavior can be confusing
- No way to un-set it (you could overwrite the header with an empty value but you can't omit it entirely)
- For more complicated types (e.g. forms), we may want to rely on the header to tells us more about the body, which is incompatible with the reverse

Another reason to leave it explicit for now is it's a non-breaking change to add the implicit behavior later.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates

An important design decision here is whether or not we should automatically set the `Content-Type` header. In the issue I wrote that we wouldn't, but during implementation I played around with both options. In the end I went with what was laid out in the issue. The pros and cons of setting it automatically:

Pros

- Convenient, reduces boilerplate

Cons

- It may be incorrect, e.g. if the user actually wants `application/geo+json`
- Implicit behavior can be confusing
- No way to un-set it (you could overwrite the header with an empty value but you can't omit it entirely)
- For more complicated types (e.g. forms), we may want to rely on the header to tells us more about the body, which is incompatible with the reverse

Another reason to leave it explicit for now is it's a non-breaking change to add the implicit behavior later.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates

The original design in the ticket was to use have the user still provide `Content-Type`, but I realized there isn't much value in that. It just requires more work for the user, and introduces another thing we need to validate (body type vs `Content-Type`). If the user needs a subset of `application/json` they can still override it. Otherwise, providing it ourselves is just more convenient. The other motivating factor behind is forms. The machinery that reqwest gives us to set form data automatically sets the header, so I think it makes sense to follow that lead.

Closes #242
LucasPickering added a commit that referenced this issue Jun 3, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates
- `Content-Type` header will be set automatically based on body type

The original design in the ticket was to use have the user still provide `Content-Type`, but I realized there isn't much value in that. It just requires more work for the user, and introduces another thing we need to validate (body type vs `Content-Type`). If the user needs a subset of `application/json` they can still override it. Otherwise, providing it ourselves is just more convenient. The other motivating factor behind is forms. The machinery that reqwest gives us to set form data automatically sets the header, so I think it makes sense to follow that lead.

Closes #242
LucasPickering added a commit that referenced this issue Jun 4, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates
- `Content-Type` header will be set automatically based on body type

The original design in the ticket was to use have the user still provide `Content-Type`, but I realized there isn't much value in that. It just requires more work for the user, and introduces another thing we need to validate (body type vs `Content-Type`). If the user needs a subset of `application/json` they can still override it. Otherwise, providing it ourselves is just more convenient. The other motivating factor behind is forms. The machinery that reqwest gives us to set form data automatically sets the header, so I think it makes sense to follow that lead.

Closes #242
LucasPickering added a commit that referenced this issue Jun 4, 2024
- Add !json tag to `body` field of recipes
- !json bodies can contain any data. Strings will be treated as templates
- `Content-Type` header will be set automatically based on body type

The original design in the ticket was to use have the user still provide `Content-Type`, but I realized there isn't much value in that. It just requires more work for the user, and introduces another thing we need to validate (body type vs `Content-Type`). If the user needs a subset of `application/json` they can still override it. Otherwise, providing it ourselves is just more convenient. The other motivating factor behind is forms. The machinery that reqwest gives us to set form data automatically sets the header, so I think it makes sense to follow that lead.

Closes #242
@LucasPickering LucasPickering added this to the 1.4.0 milestone Jun 9, 2024
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Jun 17, 2024
[1.4.0] - 2024-06-11

Added

- Structured bodies can now be defined with tags on the body field of a recipe,
making it more convenient to construct bodies of common types. Supported types
are:
  - `!json` [#242](LucasPickering/slumber#242)
  - `!form_urlencoded` [#244](LucasPickering/slumber#244)
  - `!form_multipart` [#243](LucasPickering/slumber#243)
  - [See docs](https://slumber.lucaspickering.me/book/api/request_collection/recipe_body.html) for usage instructions
- Support multiple instances of the same query param [#245](LucasPickering/slumber#245) (@maksimowiczm)
  - Query params can now be defined as a list of `<param>=<value>` entries
  - [See docs](https://slumber.lucaspickering.me/book/api/request_collection/query_parameters.html)
- Templates can now render binary values in certain contexts
  - [See docs](https://slumber.lucaspickering.me/book/user_guide/templates.html#binary-templates)

Changed

- When a modal/dialog is open `q` now exits the dialog instead of the entire app
- Upgrade to Rust 1.76

Fixed

- Fix "Unknown request ID" error showing on startup [#238](LucasPickering/slumber#238)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
qol Improvements that make usage smoother, without introducing new functionality
Projects
None yet
1 participant