Collection Reuse & Inheritance

The Problem

Let's start with an example of something that sucks. Let's say you're making requests to a fish-themed JSON API, and it requires authentication. Gotta protect your fish! Your request collection might look like so:

profiles:
  production:
    data:
      host: https://myfishes.fish
      fish_id: 6

chains:
  token:
    source: !file
      path: ./api_token.txt
  fish_id:
    source: !prompt
      message: Fish ID

requests:
  list_fish: !request
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true
    headers:
      Accept: application/json
    authentication: !bearer "{{chains.token}}"

  get_fish: !request
    method: GET
    url: "{{host}}/fishes/{{fish_id}}"
    headers:
      Accept: application/json
    authentication: !bearer "{{chains.token}}"

The Solution

You've heard of DRY, so you know this is bad. Every new request recipe requires re-specifying the headers, and if anything about the authorization changes, you have to change it in multiple places.

You can easily re-use components of your collection using YAML's merge feature.

profiles:
  production:
    data:
      host: https://myfishes.fish

chains:
  token:
    source: !file
      path: ./api_token.txt
  fish_id:
    source: !prompt
      message: Fish ID

# This is needed to tell Slumber not to complain about an unknown key
.ignore:
  # The name here is arbitrary, pick any name you like
  request_base: &request_base
    headers:
      Accept: application/json
    authentication: !bearer "{{chains.token}}"

requests:
  list_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true

  get_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes/{{chains.fish_id}}"

Great! That's so much cleaner. Now each recipe can inherit whatever base properties you want just by including <<: *request_base. This is still a bit repetitive, but it has the advantage of being explicit. You may have some requests that don't want to include those values.

Recursive Inheritance

But wait! What if you have a new request that needs an additional header? Unfortunately, YAML's merge feature does not support recursive merging. If you need to extend the headers map from the base request, you'll need to pull that map in manually:

profiles:
  production:
    data:
      host: https://myfishes.fish

chains:
  token:
    source: !file
      path: ./api_token.txt
  fish_id:
    source: !prompt
      message: Fish ID

.ignore:
  request_base: &request_base
    headers: &headers_base # This will let us pull in the header map
      Accept: application/json
    authentication: !bearer "{{chains.token}}"

requests:
  list_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true

  get_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes/{{chains.fish_id}}"

  create_fish: !request
    <<: *request_base
    method: POST
    url: "{{host}}/fishes"
    headers:
      <<: *headers_base
      Host: myfishes.fish
    body: !json { "kind": "barracuda", "name": "Jimmy" }