Chaining Requests

Let’s create your first code snippet! In the scanapi.yaml file, add the create_snippet request:

- name: create_snippet
  path: /snippets/
  method: post
  body:
    title: Hello World
    code: "print('hello world')"
    style: "xcode"
    language: "python"
  tests:
    - name: status_code_is_201
      assert: ${{ response.status_code == 201 }}

Putting it all together:

endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}

      # ALL BELOW IS NEW
      - name: create_snippet
        path: /snippets/
        method: post
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}

Run ScanAPI again and reload the report:

$ scanapi run

Report overview - failing

Failing test

Oops, the response is Unauthorized. That makes the test fail and also, we could not create the snippet code. To fix it, we need to send the Authentication Token received in the /login response in the Authorization headers of /snippets.

In the scanapi.yaml file, in the get_token request, let’s store the received key in the token variable:

vars:
  token: ${{response.json()["key"]}}

and in the create_snippet request, let’s send the token in via Authorization header:

headers:
  Authorization: Token ${token}

Putting it all together:

endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
        vars: # this is new
          token: ${{response.json()["key"]}} # this is new
      - name: create_snippet
        path: /snippets/
        method: post
        headers: # this is new
          Authorization: Token ${token} # this is new
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}

Run ScanAPI again and reload the report

Report overview - success

Response details

It works now! But, it is still missing one detail. Your key is being exposed in the request details

Request details - exposed key

Let’s also hide this information. Add the Authorization headers to the hide_request in the scanapi.conf:

report:
  hide_request:
    body:
      - password
    headers: # this is new
      - Authorization # this is new
  hide_response:
    body:
      - key

Run ScanAPI and reload the report again

Request details - hidden key

All good, the sensitive information is hidden now. Using the same idea, lets chain dynamically one more request. In the scanapi.yaml file, in the create_snippet request, let’s store the id of the created snippet:

vars:
  snippet_id: ${{response.json()["id"]}}

and let’s create a new request to get the details of your brand new snippet:

- name: snippet_details
  path: /snippets/${snippet_id}/
  method: get
  tests:
    - name: status_code_is_200
      assert: ${{ response.status_code == 200 }}

Putting it all together:

endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
        vars:
          token: ${{response.json()["key"]}}
      - name: create_snippet
        path: /snippets/
        method: post
        headers:
          Authorization: Token ${token}
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        vars: # this is new
          snippet_id: ${{response.json()["id"]}} # this is new
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}
      - name: snippet_details # this is new
        path: /snippets/${snippet_id}/ # this is new
        method: get # this is new
        tests: # this is new
          - name: status_code_is_200 # this is new
            assert: ${{ response.status_code == 200 }} # this is new

Run ScanAPI again and reload the report

Report overview - snippet details

Let’s go ahead and add more snippet requests:

endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
        vars:
          token: ${{response.json()["key"]}}
      - name: create_snippet
        path: /snippets/
        method: post
        headers:
          Authorization: Token ${token}
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        vars:
          snippet_id: ${{response.json()["id"]}}
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}
      - name: snippet_details
        path: /snippets/${snippet_id}/
        method: get
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}

      # ALL BELOW IS NEW
      - name: snippet_update_with_patch
        path: /snippets/${snippet_id}/
        method: patch
        headers:
          Authorization: Token ${token}
        body:
          code: "print('hello, patch')"
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
      - name: snippet_update_with_put
        path: /snippets/${snippet_id}/
        method: put
        headers:
          Authorization: Token ${token}
        body:
          title: Hello World - Ruby
          code: "puts 'hello world'"
          style: "emacs"
          language: "ruby"
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
      - name: delete_snippet
        path: snippets/${snippet_id}/
        method: delete
        headers:
          Authorization: Token ${token}
        tests:
          - name: status_code_is_204
            assert: ${{ response.status_code == 204 }}
      - name: snippets_list_all
        path: /snippets/
        method: get
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}

Run ScanAPI again and reload the report

Report overview - snippet details

Yay, you have finished testing and documenting the snippets requests using ScanAPI! 🎉

With the chaining requests feature, you can use any responses information from one request into the next requests via custom variables. This gives you huge flexibility and the power to test complex scenarios.

You might have noticed that the specification has a lot of repeated code. Let’s see how we can improve it using nested endpoints.