Chains
Chains are Slumber's most powerful feature. They allow you to dynamically build requests based on other responses, shell commands, and more.
Chains in Practice
The most common example of a chain is with a login request. You can define a recipe to log in to a service using username+password, then get the returned API token to authenticate subsequent requests. Of course, we don't want to store our credentials in Slumber file, so we can also use chains to fetch those. Let's see this in action:
chains:
username:
source: !file
path: ./username.txt
password:
source: !file
path: ./password.txt
auth_token:
source: !request
recipe: login
selector: $.token
requests:
# This returns a response like {"token": "abc123"}
login: !request
method: POST
url: "https://myfishes.fish/login"
body:
!json {
"username": "{{chains.username}}",
"password": "{{chains.password}}",
}
get_user: !request
method: GET
url: "https://myfishes.fish/current-user"
authentication: !bearer "{{chains.auth_token}}"
For more info on the
selector
field, see Data Filtering & Querying
Automatically Executing the Upstream Request
By default, the chained request (i.e. the "upstream" request) has to be executed manually to get the login token. You can have the upstream request automatically execute using the trigger
field:
chains:
auth_token:
source: !request
recipe: login
# Execute only if we've never logged in before
trigger: !no_history
selector: $.token
---
chains:
auth_token:
source: !request
recipe: login
# Execute only if the latest response is older than a day. Useful if your
# token expires after a fixed amount of time
trigger: !expire 1d
selector: $.token
---
chains:
auth_token:
source: !request
recipe: login
# Always execute
trigger: !always
selector: $.token
For more detail about the various trigger variants, including the syntax of the expire
variant, see the API docs.
Chaining Chains
Chains on their own are powerful enough, but what makes them really cool is that the arguments to a chain are templates in themselves, meaning you can use nested templates to chain chains to other chains! Wait, what?
Let's say the login response doesn't return JSON, but instead the response looks like this:
token:abc123
Clearly this isn't a well-designed API, but sometimes that's all you get. You can use a nested chain with cut to parse this:
chains:
username:
source: !file
path: ./username.txt
password_encrypted:
source: !file
path: ./password.txt
password:
source: !command
command:
auth_token_raw:
source: !request
recipe: login
auth_token:
source: !command
command: ["cut", "-d':'", "-f2"]
stdin: "{{chains.auth_token_raw}}"
requests:
login: !request
method: POST
url: "https://myfishes.fish/login"
body:
!json {
"username": "{{chains.username}}",
"password": "{{chains.password}}",
}
get_user: !request
method: GET
url: "https://myfishes.fish/current-user"
authentication: !bearer "{{chains.auth_token}}"
This means you can use external commands to perform any manipulation on data that you want.