Skip to content

Commit

Permalink
Store all collection DBs in a single file
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasPickering committed Dec 15, 2023
1 parent aa6439d commit 74ff0cf
Show file tree
Hide file tree
Showing 20 changed files with 537 additions and 164 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
### Changed

- [BREAKING] Key profiles/chains/requests by ID in collection file
- [BREAKING] Move request history from `slumber/{id}.sqlite` to `slumber/{id}/state.sqlite`
- Request history will be lost. If you want to recover it, you can move the old file to the new location (use `slumber show` to find the directory location)
- [BREAKING] Merge request history into a single DB file
- Request history (and UI state) will be lost
- [BREAKING] `show` subcommand now takes a `target` argument
- Right now the only option is `slumber show dir`, which has the same behavior as the old `slumber show` (except now it prints the bare directory)
- Hide sensitive chain values in preview
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ Slumber is based around **collections**. A collection is a group of request **re

```yaml
# slumber.yml
id: example
requests:
get:
method: GET
Expand Down
16 changes: 8 additions & 8 deletions docs/src/api/chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ See the [`ChainSource`](./chain_source.md) docs for more detail.

```yaml
# Load chained value from a file
id: username
source: !file ./username.txt
username:
source: !file ./username.txt
---
# Prompt the user for a value whenever the request is made
id: password
source: !prompt Enter Password
sensitive: true
password:
source: !prompt Enter Password
sensitive: true
---
# Use a value from another response
# Assume the request recipe with ID `login` returns a body like `{"token": "foo"}`
id: auth_token
source: !request login
selector: $.token
auth_token:
source: !request login
selector: $.token
```
12 changes: 6 additions & 6 deletions docs/src/api/profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ Profiles also support nested templates, via the `!template` tag.
## Examples

```yaml
id: local
name: Local
data:
host: localhost:5000
url: !template "https://{{host}}"
user_guid: abc123
local:
name: Local
data:
host: localhost:5000
url: !template "https://{{host}}"
user_guid: abc123
```
27 changes: 17 additions & 10 deletions docs/src/api/request_collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,33 @@ Whichever of those files is found _first_ will be used. If you want to use a dif
slumber -c my-collection.yml
```

## Collection ID
## Collection History & Migration

Each collection needs a unique ID (via the `id` field). This ID is used to tie the collection to its history. If the ID of a collection changes, you'll lose the history for it. If two collections share an ID, their request history could start interfering with each other. Make sure each collection used on your computer is unique.
Each collection needs a unique ID generated when the collection is first loaded by Slumber. This ID is used to persist request history and other data related to the collection. If you move a collection file, a new ID will be generated and it will be unlinked from its previous history. If you want to retain that history, you can migrate data from the old ID to the new one like so:

```sh
slumber collections migrate /slumber/old.yml /slumber/new.yml
```

If you don't remember the path of the old file, you can list all known collections with:

```sh
slumber collections list
```

## Fields

A request collection supports the following top-level fields:

| Field | Type | Description | Default |
| ---------- | ------------------------------------------------------- | ----------------------------- | -------- |
| `id` | `string` | Unique ID for this collection | Required |
| `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 | [] |

## Examples

```yaml
id: example

profiles:
local:
name: Local
Expand Down
28 changes: 14 additions & 14 deletions docs/src/api/request_recipe.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ A request recipe defines how to make a particular request. For a REST API, you'l
## Examples

```yaml
id: login
name: Login
method: POST
url: "{{host}}/anything/login"
headers:
accept: application/json
content-type: application/json
query:
root_access: yes_please
body: |
{
"username": "{{chains.username}}",
"password": "{{chains.password}}"
}
login:
name: Login
method: POST
url: "{{host}}/anything/login"
headers:
accept: application/json
content-type: application/json
query:
root_access: yes_please
body: |
{
"username": "{{chains.username}}",
"password": "{{chains.password}}"
}
```
2 changes: 0 additions & 2 deletions slumber.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
id: example

profiles:
works:
name: Works
Expand Down
47 changes: 46 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::{
/// A non-TUI command
#[derive(Clone, Debug, clap::Subcommand)]
pub enum Subcommand {
// TODO Break this apart into multiple files
/// Execute a single request
#[clap(aliases=&["req", "rq"])]
Request {
Expand Down Expand Up @@ -52,6 +53,12 @@ pub enum Subcommand {
output_file: Option<PathBuf>,
},

/// View and modify request collection history
Collections {
#[command(subcommand)]
subcommand: CollectionsSubcommand,
},

/// Show meta information about slumber
Show {
#[command(subcommand)]
Expand All @@ -65,6 +72,23 @@ pub enum ShowTarget {
Dir,
}

#[derive(Clone, Debug, clap::Subcommand)]
pub enum CollectionsSubcommand {
/// List all known request collections
#[command(visible_alias = "ls")]
List,
/// Move all data from one collection to another.
///
/// The data from the source collection will be merged into the target
/// collection, then all traces of the source collection will be deleted!
Migrate {
/// The path the collection to migrate *from*
from: PathBuf,
/// The path the collection to migrate *into*
to: PathBuf,
},
}

impl Subcommand {
/// Execute a non-TUI command
pub async fn execute(
Expand All @@ -80,6 +104,8 @@ impl Subcommand {
} => {
let collection_path =
RequestCollection::try_path(collection_override)?;
let database =
Database::load()?.into_collection(&collection_path)?;
let mut collection =
RequestCollection::load(collection_path).await?;

Expand All @@ -101,7 +127,6 @@ impl Subcommand {
)?;

// Build the request
let database = Database::load(&collection.id)?;
let overrides: IndexMap<_, _> = overrides.into_iter().collect();
let request = RequestBuilder::new(
recipe,
Expand Down Expand Up @@ -156,6 +181,8 @@ impl Subcommand {
Ok(())
}

Subcommand::Collections { subcommand } => subcommand.execute(),

Subcommand::Show { target } => {
match target {
ShowTarget::Dir => println!("{}", Directory::root()),
Expand All @@ -166,6 +193,24 @@ impl Subcommand {
}
}

impl CollectionsSubcommand {
fn execute(self) -> anyhow::Result<()> {
let database = Database::load()?;
match self {
CollectionsSubcommand::List => {
for path in database.get_collections()? {
println!("{}", path.display());
}
}
CollectionsSubcommand::Migrate { from, to } => {
database.merge_collections(&from, &to)?;
println!("Migrated {} into {}", from.display(), to.display());
}
}
Ok(())
}
}

/// Prompt the user for input on the CLI
#[derive(Debug)]
struct CliPrompter;
Expand Down
11 changes: 0 additions & 11 deletions src/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ pub struct RequestCollection<S = PathBuf> {
#[serde(skip)]
source: S,

/// Unique ID for this collection. This should be unique for across all
/// collections used on one computer.
pub id: CollectionId,
#[serde(default, deserialize_with = "cereal::deserialize_id_map")]
pub profiles: IndexMap<ProfileId, Profile>,
#[serde(default, deserialize_with = "cereal::deserialize_id_map")]
Expand All @@ -55,13 +52,6 @@ pub struct RequestCollection<S = PathBuf> {
pub recipes: IndexMap<RequestRecipeId, RequestRecipe>,
}

/// A unique ID for a collection. This is necessary to give each collection its
/// own database.
#[derive(
Clone, Debug, Default, Deref, Display, From, Serialize, Deserialize,
)]
pub struct CollectionId(String);

/// Mutually exclusive hot-swappable config group
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Profile {
Expand Down Expand Up @@ -220,7 +210,6 @@ impl<S> RequestCollection<S> {
pub fn with_source<T>(self, source: T) -> RequestCollection<T> {
RequestCollection {
source,
id: self.id,
profiles: self.profiles,
chains: self.chains,
recipes: self.recipes,
Expand Down
2 changes: 0 additions & 2 deletions src/collection/insomnia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use indexmap::IndexMap;
use serde::Deserialize;
use std::{fs::File, path::Path};
use tracing::info;
use uuid::Uuid;

impl RequestCollection<()> {
/// Convert an Insomnia exported collection into the slumber format. This
Expand Down Expand Up @@ -56,7 +55,6 @@ impl RequestCollection<()> {

Ok(RequestCollection {
source: (),
id: Uuid::new_v4().to_string().into(),
profiles,
recipes,
chains: IndexMap::new(),
Expand Down
Loading

0 comments on commit 74ff0cf

Please sign in to comment.