Skip to content

Commit

Permalink
Deny unknown fields during config/collection deserialization
Browse files Browse the repository at this point in the history
Closes #154
  • Loading branch information
LucasPickering committed Apr 18, 2024
1 parent d049df0 commit 7e51d9c
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 28 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
- Profile values are always treated as templates now
- Any profile values that were previously the "raw" variant (the default) that contain template syntax (e.g. `{{user_id}}`) will now be rendered as templates. In reality this is very unlikely, so this probably isn't going to break your setup
- If you have an existing profile value tagged with `!template` it **won't** break, but it will no longer do anything
- Unknown fields in config/collection files will now be rejected ([#154](https://github.com/LucasPickering/slumber/issues/154))
- In most cases this field is a mistake, so this is meant to make debugging easier
- If you have an intentional unknown field, you can now nest it under `.ignore` to ignore it

### Added

Expand Down
22 changes: 12 additions & 10 deletions docs/src/api/request_collection/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ slumber collections list

A request collection supports the following top-level fields:

| Field | Type | Description | Default |
| ---------- | ------------------------------------------------------- | ------------------------- | ------- |
| `profiles` | [`mapping[string, Profile]`](./profile.md) | Static template values | `{}` |
| `requests` | [`mapping[string, RequestRecipe]`](./request_recipe.md) | Requests Slumber can send | `{}` |
| `chains` | [`mapping[string, Chain]`](./chain.md) | Complex template values | `{}` |
| Field | Type | Description | Default |
| ---------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `profiles` | [`mapping[string, Profile]`](./profile.md) | Static template values | `{}` |
| `requests` | [`mapping[string, RequestRecipe]`](./request_recipe.md) | Requests Slumber can send | `{}` |
| `chains` | [`mapping[string, Chain]`](./chain.md) | Complex template values | `{}` |
| `.ignore` | Any | Extra data to be ignored by Slumber (useful with [YAML anchors](https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/)) | |

## Examples

Expand Down Expand Up @@ -71,11 +72,12 @@ chains:
recipe: login
selector: $.token

# Use YAML anchors for de-duplication
base: &base
headers:
Accept: application/json
Content-Type: application/json
# Use YAML anchors for de-duplication (Anything under .ignore is ignored)
.ignore:
base: &base
headers:
Accept: application/json
Content-Type: application/json

requests:
login: !request
Expand Down
9 changes: 5 additions & 4 deletions docs/src/user_guide/chaining_requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ chains:
recipe: login
selector: $.token

base: &base
headers:
Accept: application/json
Content-Type: application/json
.ignore:
base: &base
headers:
Accept: application/json
Content-Type: application/json

requests:
login: !request
Expand Down
9 changes: 5 additions & 4 deletions docs/src/user_guide/filter_query.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ chains:
selector: $.token

# Use YAML anchors for de-duplication
base: &base
headers:
Accept: application/json
Content-Type: application/json
.ignore:
base: &base
headers:
Accept: application/json
Content-Type: application/json

requests:
login: !request
Expand Down
11 changes: 6 additions & 5 deletions slumber.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ chains:
trigger: !expire 12h
selector: $.headers["X-Amzn-Trace-Id"]

base: &base
authentication: !bearer "{{chains.auth_token}}"
headers:
Accept: application/json
Content-Type: application/json
.ignore:
base: &base
authentication: !bearer "{{chains.auth_token}}"
headers:
Accept: application/json
Content-Type: application/json

requests:
login: !request
Expand Down
1 change: 1 addition & 0 deletions src/collection/insomnia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ impl Collection {
profiles,
recipes,
chains: IndexMap::new(),
_ignore: serde::de::IgnoredAny,
})
}
}
Expand Down
17 changes: 14 additions & 3 deletions src/collection/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::{path::PathBuf, time::Duration};
/// of configuration.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(deny_unknown_fields)]
pub struct Collection {
#[serde(default, deserialize_with = "cereal::deserialize_id_map")]
pub profiles: IndexMap<ProfileId, Profile>,
Expand All @@ -27,11 +28,18 @@ pub struct Collection {
/// intuitive
#[serde(default, rename = "requests")]
pub recipes: RecipeTree,
/// A hack-ish to allow users to add arbitrary data to their collection
/// file without triggering a unknown field error. Ideally we could
/// ignore anything that starts with `.` (recursively) but that
/// requires a custom serde impl for each type, or changes to the macro
#[serde(skip_serializing, rename = ".ignore")]
pub(super) _ignore: serde::de::IgnoredAny,
}

/// Mutually exclusive hot-swappable config group
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(deny_unknown_fields)]
pub struct Profile {
#[serde(skip)] // This will be auto-populated from the map key
pub id: ProfileId,
Expand All @@ -57,6 +65,7 @@ pub struct ProfileId(String);
/// A gathering of like-minded recipes and/or folders
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(deny_unknown_fields)]
pub struct Folder {
#[serde(skip)] // This will be auto-populated from the map key
pub id: RecipeId,
Expand All @@ -76,6 +85,7 @@ pub struct Folder {
/// meaning related to string interpolation.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(deny_unknown_fields)]
pub struct Recipe {
#[serde(skip)] // This will be auto-populated from the map key
pub id: RecipeId,
Expand Down Expand Up @@ -112,7 +122,7 @@ pub struct RecipeId(String);
/// request twice.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum Authentication {
/// `Authorization: Basic {username:password | base64}`
Basic {
Expand All @@ -128,6 +138,7 @@ pub enum Authentication {
/// can use it in a template via `{{chains.<chain_id>}}`.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(deny_unknown_fields)]
pub struct Chain {
#[serde(skip)] // This will be auto-populated from the map key
pub id: ChainId,
Expand Down Expand Up @@ -186,7 +197,7 @@ impl Equivalent<ChainId> for ChainId<&str> {
/// The source of data for a chain
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum ChainSource {
/// Load data from the most recent response of a particular request recipe
Request {
Expand All @@ -207,7 +218,7 @@ pub enum ChainSource {
/// dependency request.
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum ChainRequestTrigger {
/// Never trigger the request. This is the default because upstream
/// requests could be mutating, so we want the user to explicitly opt into
Expand Down
2 changes: 1 addition & 1 deletion src/collection/recipe_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub struct RecipeLookupKey(Vec<RecipeId>);
/// A node in the recipe tree, either a folder or recipe
#[derive(Clone, Debug, From, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
#[allow(clippy::large_enum_variant)]
pub enum RecipeNode {
Folder(Folder),
Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use tracing::info;
/// are made to the config file while a session is running, they won't be
/// picked up until the app restarts.
#[derive(Debug, Deserialize)]
#[serde(default)]
#[serde(default, deny_unknown_fields)]
pub struct Config {
/// The path that the config was loaded from, or tried to be loaded from if
/// the file didn't exist
Expand Down

0 comments on commit 7e51d9c

Please sign in to comment.