diff --git a/CHANGELOG.md b/CHANGELOG.md index fbf25cab..24232d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,7 @@ ### Breaking -- Rename top-level collection field `requests` to `recipes` -- All existing recipes must be tagged with `!recipe` in the collection file +- All existing recipes must be tagged with `!request` in the collection file - This is necessary to differentiate from the new `!folder` type - 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 diff --git a/README.md b/README.md index 612bc1a0..4460e022 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Slumber is based around **collections**. A collection is a group of request **re ```yaml # slumber.yml -recipes: - get: !recipe +requests: + get: !request method: GET url: https://httpbin.org/get ``` diff --git a/docs/src/api/request_collection/chain.md b/docs/src/api/request_collection/chain.md index 5dbe7251..957020c5 100644 --- a/docs/src/api/request_collection/chain.md +++ b/docs/src/api/request_collection/chain.md @@ -13,7 +13,7 @@ To use a chain in a template, reference it as `{{chains.}}`. | `selector` | [`JSONPath`](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) | Selector to transform/narrow down results in a chained value. See [Filtering & Querying](../../user_guide/filter_query.md) | `null` | | `content_type` | [`ContentType`](./content_type.md) | Force content type. Not required for `request` and `file` chains, as long as the `Content-Type` header/file extension matches the data | | -See the [`ChainSource`](./chain_source.md) docs for more detail. +See the [`ChainSource`](./chain_source.md) docs for detail on the different types of chainable values. ## Examples diff --git a/docs/src/api/request_collection/index.md b/docs/src/api/request_collection/index.md index 340d08f7..129dc5ad 100644 --- a/docs/src/api/request_collection/index.md +++ b/docs/src/api/request_collection/index.md @@ -77,8 +77,8 @@ base: &base Accept: application/json Content-Type: application/json -recipes: - login: !recipe +requests: + login: !request <<: *base method: POST url: "{{host}}/anything/login" @@ -90,15 +90,15 @@ recipes: # Folders can be used to keep your recipes organized users: !folder - children: - get_user: !recipe + requests: + get_user: !request <<: *base name: Get User method: GET url: "{{host}}/anything/current-user" authentication: !bearer "{{chains.auth_token}}" - update_user: !recipe + update_user: !request <<: *base name: Update User method: PUT diff --git a/docs/src/api/request_collection/request_recipe.md b/docs/src/api/request_collection/request_recipe.md index bf1663ff..48a0bf16 100644 --- a/docs/src/api/request_collection/request_recipe.md +++ b/docs/src/api/request_collection/request_recipe.md @@ -8,7 +8,7 @@ Recipes can be organized into folders. This means your set of recipes can form a ## Recipe Fields -The tag for a recipe is `!recipe` (see examples). +The tag for a recipe is `!request` (see examples). | Field | Type | Description | Default | | ---------------- | -------------------------------------------- | --------------------------------- | ---------------------- | @@ -32,7 +32,7 @@ The tag for a folder is `!folder` (see examples). ## Examples ```yaml -login: !recipe +login: !request name: Login method: POST url: "{{host}}/anything/login" @@ -49,14 +49,14 @@ login: !recipe --- fish: !folder name: Users - children: - create_fish: !recipe + requests: + create_fish: !request method: POST url: "{{host}}/fishes" body: > {"kind": "barracuda", "name": "Jimmy"} - list_fish: !recipe + list_fish: !request method: GET url: "{{host}}/fishes" query: diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md index a46d99ce..9330c16f 100644 --- a/docs/src/getting_started.md +++ b/docs/src/getting_started.md @@ -9,13 +9,13 @@ Once you've [installed Slumber](/artifacts), setup is easy. Create a file called `slumber.yml` and add the following contents: ```yaml -recipes: - get: !recipe +requests: + get: !request method: GET url: https://httpbin.org/get ``` -> Note: the `!recipe` tag, which tells Slumber that this is a request recipe, not a folder. This is [YAML's tag syntax](https://yaml.org/spec/1.2.2/#24-tags), which is used commonly throughout Slumber to provide explicit configuration. +> Note: the `!request` tag, which tells Slumber that this is a request recipe, not a folder. This is [YAML's tag syntax](https://yaml.org/spec/1.2.2/#24-tags), which is used commonly throughout Slumber to provide explicit configuration. ### 2. Run Slumber @@ -37,14 +37,14 @@ profiles: data: host: https://myfishes.fish -recipes: - create_fish: !recipe +requests: + create_fish: !request method: POST url: "{{host}}/fishes" body: > {"kind": "barracuda", "name": "Jimmy"} - list_fish: !recipe + list_fish: !request method: GET url: "{{host}}/fishes" query: diff --git a/docs/src/user_guide/chaining_requests.md b/docs/src/user_guide/chaining_requests.md index 7c800208..21da592a 100644 --- a/docs/src/user_guide/chaining_requests.md +++ b/docs/src/user_guide/chaining_requests.md @@ -14,8 +14,8 @@ base: &base Accept: application/json Content-Type: application/json -recipes: - login: !recipe +requests: + login: !request <<: *base method: POST url: "https://myfishes.fish/login" @@ -25,7 +25,7 @@ recipes: "password": "password" } - get_user: !recipe + get_user: !request <<: *base method: GET url: "https://myfishes.fish/current-user" diff --git a/docs/src/user_guide/cli.md b/docs/src/user_guide/cli.md index ad98aeee..cc8f76a5 100644 --- a/docs/src/user_guide/cli.md +++ b/docs/src/user_guide/cli.md @@ -22,8 +22,8 @@ profiles: data: host: https://myfishes.fish -recipes: - list_fish: !recipe +requests: + list_fish: !request method: GET url: "{{host}}/fishes" query: diff --git a/docs/src/user_guide/filter_query.md b/docs/src/user_guide/filter_query.md index 244d5cd4..4bea1503 100644 --- a/docs/src/user_guide/filter_query.md +++ b/docs/src/user_guide/filter_query.md @@ -45,8 +45,8 @@ base: &base Accept: application/json Content-Type: application/json -recipes: - login: !recipe +requests: + login: !request <<: *base method: POST url: "https://myfishes.fish/anything/login" @@ -56,7 +56,7 @@ recipes: "password": "{{chains.password}}" } - get_user: !recipe + get_user: !request <<: *base method: GET url: "https://myfishes.fish/anything/current-user" diff --git a/docs/src/user_guide/inheritance.md b/docs/src/user_guide/inheritance.md index 84e95096..662b448f 100644 --- a/docs/src/user_guide/inheritance.md +++ b/docs/src/user_guide/inheritance.md @@ -16,8 +16,8 @@ chains: source: !file path: ./api_token.txt -recipes: - list_fish: !recipe +requests: + list_fish: !request method: GET url: "{{host}}/fishes" query: @@ -26,7 +26,7 @@ recipes: Accept: application/json authentication: !bearer "{{chains.token}}" - get_fish: !recipe + get_fish: !request method: GET url: "{{host}}/fishes/{{fish_id}}" headers: @@ -57,15 +57,15 @@ request_base: &request_base Accept: application/json authentication: !bearer "{{chains.token}}" -recipes: - list_fish: !recipe +requests: + list_fish: !request <<: *request_base method: GET url: "{{host}}/fishes" query: big: "true" - get_fish: !recipe + get_fish: !request <<: *request_base method: GET url: "{{host}}/fishes/{{chains.fish_id}}" @@ -94,20 +94,20 @@ request_base: &request_base Accept: application/json authentication: !bearer "{{chains.token}}" -recipes: - list_fish: !recipe +requests: + list_fish: !request <<: *request_base method: GET url: "{{host}}/fishes" query: big: "true" - get_fish: !recipe + get_fish: !request <<: *request_base method: GET url: "{{host}}/fishes/{{chains.fish_id}}" - create_fish: !recipe + create_fish: !request <<: *request_base method: POST url: "{{host}}/fishes" diff --git a/docs/src/user_guide/templates.md b/docs/src/user_guide/templates.md index 4e6a55ab..b86b26fd 100644 --- a/docs/src/user_guide/templates.md +++ b/docs/src/user_guide/templates.md @@ -23,8 +23,8 @@ profiles: data: host: https://myfishes.fish -recipes: - list_fish: !recipe +requests: + list_fish: !request method: GET url: "{{host}}/fishes" query: @@ -65,14 +65,14 @@ chains: # https://jsonpath.com/ selector: $.id -recipes: - create_fish: !recipe +requests: + create_fish: !request method: POST url: "{{host}}/fishes" body: > {"kind": "barracuda", "name": "Jimmy"} - get_fish: !recipe + get_fish: !request method: GET url: "{{host}}/fishes/{{chains.fish_id}}" ``` @@ -107,14 +107,14 @@ chains: recipe: create_fish selector: $.id -recipes: - create_fish: !recipe +requests: + create_fish: !request method: POST url: "{{host}}/fishes" body: > {"kind": "barracuda", "name": "Jimmy"} - get_fish: !recipe + get_fish: !request method: GET url: "{{host}}/fishes/{{fish_id}}" ``` diff --git a/slumber.yml b/slumber.yml index 6441aae2..9c7ff2a8 100644 --- a/slumber.yml +++ b/slumber.yml @@ -35,8 +35,8 @@ base: &base Accept: application/json Content-Type: application/json -recipes: - login: !recipe +requests: + login: !request method: POST url: "{{host}}/anything/login" query: @@ -53,8 +53,8 @@ recipes: users: !folder name: Users - children: - get_users: !recipe + requests: + get_users: !request <<: *base name: Get Users method: GET @@ -62,13 +62,13 @@ recipes: query: foo: bar - get_user: !recipe + get_user: !request <<: *base name: Get User method: GET url: "{{host}}/anything/{{user_guid}}" - modify_user: !recipe + modify_user: !request <<: *base name: Modify User method: PUT @@ -78,14 +78,14 @@ recipes: "username": "new username" } - get_image: !recipe + get_image: !request headers: Accept: image/png name: Get Image method: GET url: "{{host}}/image" - delay: !recipe + delay: !request <<: *base name: Delay method: GET diff --git a/src/collection/models.rs b/src/collection/models.rs index 96f783b5..8c8d60d4 100644 --- a/src/collection/models.rs +++ b/src/collection/models.rs @@ -23,7 +23,9 @@ pub struct Collection { pub profiles: IndexMap, #[serde(default, deserialize_with = "cereal::deserialize_id_map")] pub chains: IndexMap, - #[serde(default)] + /// Internally we call these recipes, but to a user `requests` is more + /// intuitive + #[serde(default, rename = "requests")] pub recipes: RecipeTree, } @@ -59,8 +61,12 @@ pub struct Folder { #[serde(skip)] // This will be auto-populated from the map key pub id: RecipeId, pub name: Option, - /// RECURSION - #[serde(default, deserialize_with = "cereal::deserialize_id_map")] + /// RECURSION. Use `requests` in serde to match the root field. + #[serde( + default, + deserialize_with = "cereal::deserialize_id_map", + rename = "requests" + )] pub children: IndexMap, } diff --git a/src/collection/recipe_tree.rs b/src/collection/recipe_tree.rs index 249ce931..dc671a85 100644 --- a/src/collection/recipe_tree.rs +++ b/src/collection/recipe_tree.rs @@ -36,6 +36,9 @@ pub struct RecipeLookupKey(Vec); #[allow(clippy::large_enum_variant)] pub enum RecipeNode { Folder(Folder), + /// Rename this variant to match the `requests` field in the root and + /// folders + #[serde(rename = "request")] Recipe(Recipe), } @@ -301,7 +304,7 @@ mod tests { ( "dupe", tagged_mapping( - "!recipe", + "!request", [("method", "GET".into()), ("url", "url".into())], ), ), @@ -310,11 +313,11 @@ mod tests { tagged_mapping( "!folder", [( - "children", + "requests", mapping([( "dupe", tagged_mapping( - "!recipe", + "!request", [ ("method", "GET".into()), ("url", "url".into()), @@ -334,7 +337,7 @@ mod tests { tagged_mapping( "!folder", [( - "children", + "requests", mapping([("dupe", tagged_mapping("!folder", []))]), )], ), @@ -350,9 +353,9 @@ mod tests { tagged_mapping( "!folder", [( - "children", + "requests", tagged_mapping( - "!recipe", + "!request", [("dupe", tagged_mapping("!folder", []))], ), )], @@ -361,7 +364,7 @@ mod tests { ( "dupe", tagged_mapping( - "!recipe", + "!request", [("method", "GET".into()), ("url", "url".into())], ), ), @@ -392,7 +395,7 @@ mod tests { // Create equivalent YAML let recipe_value: Value = tagged_mapping( - "!recipe", + "!request", [("method", "GET".into()), ("url", "http://localhost".into())], ); let yaml = mapping([ @@ -402,14 +405,14 @@ mod tests { tagged_mapping( "!folder", [( - "children", + "requests", mapping([ ( "f2", tagged_mapping( "!folder", [( - "children", + "requests", mapping([("r2", recipe_value.clone())]), )], ), diff --git a/src/tui/view/component/recipe_list.rs b/src/tui/view/component/recipe_list.rs index 139b2ddf..8387003c 100644 --- a/src/tui/view/component/recipe_list.rs +++ b/src/tui/view/component/recipe_list.rs @@ -49,7 +49,7 @@ impl RecipeListPane { .iter() .map(|(lookup_key, node)| RecipeListItem { node: node.clone(), - depth: lookup_key.len(), + depth: lookup_key.len() - 1, }) .collect_vec(); @@ -121,7 +121,23 @@ impl Generate for &RecipeListItem { where Self: 'this, { - // Apply indentation - format!("{:width$}{}", ' ', self.node.name(), width = self.depth).into() + // Apply indentation based on folder depth + let content = format!( + "{indent:width$}{name}", + indent = "", + name = self.node.name(), + width = self.depth + ); + + let theme = &TuiContext::get().theme; + let style = match self.node { + RecipeNode::Folder(_) => theme.recipe_list.folder, + RecipeNode::Recipe(_) => theme.recipe_list.recipe, + }; + + Span { + content: content.into(), + style, + } } } diff --git a/src/tui/view/theme.rs b/src/tui/view/theme.rs index 1346fa34..7892fe90 100644 --- a/src/tui/view/theme.rs +++ b/src/tui/view/theme.rs @@ -6,6 +6,7 @@ use ratatui::style::{Color, Modifier, Style}; pub struct Theme { pub pane: ThemePane, pub list: ThemeList, + pub recipe_list: ThemeRecipeList, pub tab: ThemeTab, pub table: ThemeTable, pub template_preview: ThemeTemplatePreview, @@ -21,12 +22,22 @@ impl Theme { pub const ERROR_COLOR: Color = Color::Red; } +/// Styles for List component #[derive(Debug)] pub struct ThemeList { /// Highlighted item in a list pub highlight: Style, } +#[derive(Debug)] +pub struct ThemeRecipeList { + /// Folders in recipe list + pub folder: Style, + /// Recipes in recipe list + pub recipe: Style, +} + +/// Styles for Pane component #[derive(Debug)] pub struct ThemePane { /// Pane border when not selected/focused @@ -35,12 +46,14 @@ pub struct ThemePane { pub border_selected: Style, } +/// Styles for Tab component #[derive(Debug)] pub struct ThemeTab { /// Highlighted tab in a tab group pub highlight: Style, } +/// Styles for Table component #[derive(Debug)] pub struct ThemeTable { /// Table column header text @@ -52,6 +65,7 @@ pub struct ThemeTable { pub title: Style, } +/// Styles for TemplatePreview component #[derive(Debug)] pub struct ThemeTemplatePreview { pub text: Style, @@ -65,6 +79,7 @@ pub struct ThemeText { pub highlight: Style, } +/// Styles for TextBox component #[derive(Debug)] pub struct ThemeTextBox { pub text: Style, @@ -73,6 +88,7 @@ pub struct ThemeTextBox { pub invalid: Style, } +/// Styles for TextWindow component #[derive(Debug)] pub struct ThemeTextWindow { /// Line numbers on large text areas @@ -94,6 +110,10 @@ impl Default for Theme { .fg(Color::Black) .add_modifier(Modifier::BOLD), }, + recipe_list: ThemeRecipeList { + folder: Style::default().fg(Color::Blue), + recipe: Style::default().fg(Self::PRIMARY_COLOR), + }, tab: ThemeTab { highlight: Style::default() .fg(Self::PRIMARY_COLOR) diff --git a/test_data/insomnia_imported.yml b/test_data/insomnia_imported.yml index 5908b167..fd976dae 100644 --- a/test_data/insomnia_imported.yml +++ b/test_data/insomnia_imported.yml @@ -12,8 +12,8 @@ profiles: data: host: https://httpbin.org chains: {} -recipes: - req_3bc2de939f1a4d1ebc00835cbefd6b5d: !recipe +requests: + req_3bc2de939f1a4d1ebc00835cbefd6b5d: !request name: Login method: POST url: https://httpbin.org/anything/login @@ -24,8 +24,8 @@ recipes: fld_2fa209e604774c8db800304ad38e68d0: !folder name: My Folder - children: - req_bff9461c4e81463b890856a283d5e2f3: !recipe + requests: + req_bff9461c4e81463b890856a283d5e2f3: !request name: With Body method: POST url: https://httpbin.org/post @@ -37,8 +37,8 @@ recipes: fld_2b83df7de20d446f91d94dcb0b66c06f: !folder name: Inner Folder - children: - req_467d7ec5ceee4893aa443b09e25c6e53: !recipe + requests: + req_467d7ec5ceee4893aa443b09e25c6e53: !request name: No Auth method: GET url: https://httpbin.org/get @@ -47,7 +47,7 @@ recipes: query: {} headers: {} - req_30f68ae3069b4ec59707bf793a4b74cb: !recipe + req_30f68ae3069b4ec59707bf793a4b74cb: !request name: Bearer Auth method: GET url: https://httpbin.org/get @@ -56,7 +56,7 @@ recipes: query: {} headers: {} - req_b537f7f12d2f4dffbda12fdff2ff3704: !recipe + req_b537f7f12d2f4dffbda12fdff2ff3704: !request name: Basic Auth method: GET url: https://httpbin.org/get