Stop writing scripts to interact with your APIs. Call them as CLIs instead.

Overview

Zum

Stop writing scripts to interact with your APIs. Call them as CLIs instead.

PyPI - Version Tests Coverage Linters

Zum (German word roughly meaning "to the" or "to" depending on the context, pronounced /tsʊm/) is a tool that lets you describe a web API using a TOML file and then interact with that API using your command line. This means that the days of writing custom scripts to help you interact and develop each of your APIs are over. Just create a zum.toml, describe your API and forget about maintaining more code!

Why Zum?

While there are tools out there with goals similar to zum, the scopes are quite different. The common contenders are OpenAPI-based tools (like SwaggerUI) and cURL. To me, using an OpenAPI-based documentation tool is essential on any large enough API, but the description method is very verbose and quite complex, so often times it is added once the API has quite a few endpoints. On the other hand, cURL gets very verbose and tedious very fast when querying APIs, so I don't like to use it when developing my APIs. As a comparison, here's a curl command to query a local endpoint with a JSON body:

curl --header "Content-Type: application/json" \
    --request POST \
    --data '{"name": "Dani", "city": "Santiago"}' \
    http://localhost:8000/living-beings

And here is the zum command to achieve the same result:

zum create application/json Dani Santiago

Now, imagine having to run this command hundreads of times during API development changing only the values on the request body, for example. You can see how using cURL is not ideal.

The complete documentation is available on the official website.

Installation

Install using pip!

pip install zum

Usage

Basic Usage

The basic principle is simple:

  1. Describe your API using a zum.toml file.
  2. Use the zum CLI to interact with your API.

We get more in-depth with how to structure the zum.toml file and how to use the zum CLI on the complete documentation, but for now let's see a very basic example. Imagine that you are developing an API that gets the URL of a song on YouTube. This API, for now, has only 1 endpoint: GET /song (clearly a WIP). To describe your API, you would have to write a zum.toml file similar to this one:

[metadata]
server = "http://localhost:8000"

[endpoints.dada]
route = "/song"
method = "get"

Now, to get your song's URL, all you need to do is to run:

zum dada

Notice that, after the zum command, we passed an argument, that in this case was dada. This argument tells zum that it should interact with the endpoint described on the dada endpoint section, denoted by the header [endpoints.dada]. As a rule, to access an endpoint described by the header [endpoints.{my-endpoint-name}], you will call the zum command with the {my-endpoint-name} argument:

zum {my-endpoint-name}

params, headers and body

Beware! There are some nuances on these attribute definitions, so reading the complete documentation is highly recommended.

The params of an endpoint

On the previous example, the route was static, which means that zum will always query the same route. For some things, this might not be the best of ideas (for example, for querying entities on REST APIs), and you might want to interpolate a value on the route string. Let's say that there's a collection of songs, and you wanted to get the song with id 57. Your endpoint definition should look like the following:

[endpoints.get-song]
route = "/songs/{id}"
method = "get"
params = ["id"]

As you can see, the element inside params matches the element inside the brackets on the route. This means that whatever parameter you pass to the zum CLI, it will be replaced on the route on-demand:

zum get-song 57

Now, zum will send a GET HTTP request to http://localhost:8000/songs/57. Pretty cool!

The headers of an endpoint

The headers are defined exactly the same as the params. Let's see a small example to illustrate how to use them. Imagine that you have an API that requires JWT authorization to GET the songs of its catalog. Let's define that endpoint:

[endpoints.get-authorized-catalog]
route = "/catalog"
method = "get"
headers = ["Authorization"]

Now, to acquire the catalog, we would need to run:

zum get-authorized-catalog "Bearer super-secret-token"

::: warning Warning Notice that, for the first time, we surrounded something with quotes on the CLI. The reason we did this is that, without the quotes, the console has no way of knowing if you want to pass a parameter with a space in the middle or if you want to pass multiple parameters, so it defaults to receiving the words as multiple parameters. To stop this from happening, you can surround the string in quotes, and now the whole string will be interpreted as only one parameter with the space in the middle of the string. This will be handy on future examples, so keep it in mind. :::

This will send a GET request to http://localhost:8000/catalog with the following headers:

{
    "Authorization": "Bearer super-secret-token"
}

And now you have your authorization-protected music catalog!

The body of an endpoint

Just like params and headers, the body (the body of the request) gets defined as an array:

[endpoints.create-living-being]
route = "/living-beings"
method = "post"
body = ["name", "city"]

To run this endpoint, you just need to run:

zum create-living-being Dani Santiago

This will send a POST request to http://localhost:8000/living-beings with the following request body:

{
    "name": "Dani",
    "city": "Santiago"
}

Notice that you can also cast the parameters to different types. You can read more about this on the complete documentation's section about the request body

Combining params, headers and body

Of course, sometimes you need to use some params, some headers and a body. For example, if you wanted to create a song inside an authorization-protected album (a nested entity), you would need to use the album's id as a param, the "Authorization" key inside the headers to get the authorization and the new song's data as the body. For this example, the song has a name (which is a string) and a duration in seconds (which is an integer). Let's describe this situation!

[endpoints.create-song]
route = "/albums/{id}/songs"
method = "post"
params = ["id"]
headers = ["Authorization"]
body = [
    "name",
    { name = "duration", type = "integer" }
]

Now, you can call the endpoint using:

zum create-song 8 "Bearer super-secret-token" "Con Altura" 161

This will call POST /albums/8/songs with the following headers:

{
    "Authorization": "Bearer super-secret-token"
}

And the following request body:

{
    "name": "Con Altura",
    "duration": 161
}

As you can probably tell, zum receives the params first on the CLI, then the headers and then the body. In pythonic terms, what zum does is that it kind of unpacks the three arrays consecutively, something like the following:

arguments = [*params, *headers, *body]
zum(arguments)

Developing

Clone the repository:

git clone https://github.com/daleal/zum.git

cd zum

Recreate environment:

make get-poetry
make build-env

Run the linters:

make black flake8 isort mypy pylint

Run the tests:

make tests

Resources

Comments
  • Add 'number' as a body value type

    Add 'number' as a body value type

    Feature: Add 'number' as a body value type

    Description

    JSON does not define 'integer' and 'float' as valid data types, but rather merges them both into the 'number' data type. This PR includes 'number' as part of the data types, but does not remove 'integer' and 'float' for backward compatibility.

    I think that 'array' and 'object' should also be allowed (somehow).

    Requirements

    None.

    Additional changes

    None.

    feature wontfix 
    opened by ariel-m-s 2
  • Add type support to the body parameters

    Add type support to the body parameters

    Feature: Add type support to the body parameters

    Description

    Now, the request body parameters can be casted on request. To do that, you can specify the type to cast using the following syntax:

    body = [
        { name = "parameter1", type = "integer" },
        { name = "parameter2", type = "boolean" }
    ]
    

    The possible types can be found at zum/constants.py, on the variable REQUEST_BODY_VALUE_TYPES. Note that you can still declare only the name of the variable as a string instead of declaring the variable as an object. You can even declare the object without declaring its type. An example would be:

    body = [
        "parameter1",
        { name = "parameter2", type = "float" },
        { name = "parameter3" }
    ]
    

    On that example, parameter1 will be a string, parameter2 will be a float and parameter3 will also be a string.

    Closes #6.

    Requirements

    None.

    Additional changes

    None.

    feature 
    opened by daleal 1
  • Add a test battery

    Add a test battery

    Chore: Add a test battery

    Description

    This Pull Request adds quite a few tests to improve the coverage and assure some level of security over the functionalities. This tests mainly cover the configs and requests modules. The executor, engine and cli modules are still untested.

    Closes part of #7.

    Requirements

    None.

    Additional changes

    Some validations were added to the config validators.

    chore tests 
    opened by daleal 1
  • Fix `--version` command

    Fix `--version` command

    Bugfix: Fix --version command

    Description

    This Pull Request fixes the --version command by catching the config exceptions on the engine initialization and re-raising them on the engine execution. This allows the --version command to run, even if there is no config file.

    Closes #10.

    Requirements

    None.

    Additional changes

    None.

    bugfix 
    opened by daleal 1
  • Add documentation

    Add documentation

    Docs: Add documentation

    Description

    This Pull Request adds some documentation to the project, mainly about the zum.toml file.

    Requirements

    None.

    Additional changes

    Update Poetry's version on the Makefile.

    documentation 
    opened by daleal 1
  • Re-architect zum

    Re-architect zum

    Chore: Re-architect zum

    Description

    This Pull Request re-writes almost the whole codebase to be a bit more flexible. This is by no means a clean version of the library yet, but it is now quite a bit more structured compared to how it was written before.

    Requirements

    None.

    Additional changes

    Some classes were renamed (for example, Executor is now called Engine). Now the engine saves a state that can be retrieved, instead of directly printing the output to the console.

    chore design refactor 
    opened by daleal 1
  • Add support for URL parameters and JSON body (strings only)

    Add support for URL parameters and JSON body (strings only)

    Feature: Add support for URL parameters and JSON body (strings only)

    Description

    Now, zum can interpolate URL params directly and send a body with the request (for now, only strings are sent).

    Requirements

    None.

    Additional changes

    None.

    feature 
    opened by daleal 1
  • Fix punctuation typos

    Fix punctuation typos

    Chore: Fix minor typos in punctuation marks

    Description

    Add a missing period (.) to README.md and remove a period from CONTRIBUTING.md.

    Requirements

    None.

    Additional changes

    None.

    documentation 
    opened by ariel-m-s 0
  • Documentation changes

    Documentation changes

    Docs: Documentation changes

    Description

    Fix some README errors and add a CONTRIBUTING.md file to the repo.

    Requirements

    None.

    Additional changes

    None.

    documentation 
    opened by daleal 0
  • Add CLI param for config file

    Add CLI param for config file

    Feature: Add CLI param for config file

    Description

    This Pull Request adds the option for a filename to be passed through the CLI.

    Closes #39.

    Requirements

    None.

    Additional changes

    Some very small refactoring occured, and so some tests were moved.

    feature 
    opened by daleal 0
  • Fix a README inline code block

    Fix a README inline code block

    Docs: Fix an inline code block on the README

    Description

    There's a small typo on a GET. Should be GET and was GET

    Requirements

    None.

    Additional changes

    None.

    documentation 
    opened by naquiroz 0
  • JSONDecodeError upon empty response body

    JSONDecodeError upon empty response body

    A JSONDecodeError (from the built-in json library) is raised for HTTP responses with an empty body (which is, naturally, not a JSON). I don’t think this is the expected behavior.

    json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
    

    I guess the expected behavior should be to return an empty string.

    bug 
    opened by ariel-m-s 3
  • Add CLI default help message

    Add CLI default help message

    I think that showing a help message when using the CLI in a wrong way would be helpful. For instance, when typing zum (without any arguments), the CLI currently outputs an exception (as shown bellow). I find this to be a little aggresive for newcomers (like me) who are beginning to experiment with Zum.

    Traceback (most recent call last):
      File "/usr/local/bin/zum", line 8, in <module>
        sys.exit(dispatcher())
      File "/usr/local/lib/python3.9/site-packages/zum/cli.py", line 22, in dispatcher
        engine.execute(parsed_args.action[0], parsed_args.params)  # pragma: nocover
    AttributeError: 'Namespace' object has no attribute 'action'
    

    This help message should’t be as complete as the documentation, but should provide some practical information such as a list of commands and possible arguments.

    proposal 
    opened by ariel-m-s 2
  • Implement support for using a JSON file as request body.

    Implement support for using a JSON file as request body.

    [Feature]: Support for using files as request bodies. May resolve issue #8

    Description

    This pull request adds support for using the contents of a file as the JSON body of a request. This is currently done by providing a bodyPath variable in the zum.toml file. If the file path exists and contains a file, the contents of the file are read and parsed as JSON. The contents of the file are then sent with the request.

    If the user provides both a body and bodyPath variable like:

    [endpoints.test-post]
    route = "/test-post"
    method = "post"
    body = ["arg1", "arg2"]
    bodyPath = "/some/path/to/request_body_file"
    

    the contents of body are ignored and the file located at bodyPath is used instead.

    Requirements

    None.

    Additional changes

    Added tests for the new functionality.

    feature 
    opened by sehnsucht13 1
  • Expose request timeouts

    Expose request timeouts

    It would be nice to have control over the request timeouts. I think that a default metadata could be added and each endpoint could ovewrite it if specified.

    feature proposal 
    opened by daleal 0
Releases(0.3.0)
Owner
Daniel Leal
Software Engineer at NotCo and Computer Science Student
Daniel Leal
Autodrive is designed to make it as easy as possible to interact with the Google Drive and Sheets APIs via Python

Autodrive Autodrive is designed to make it as easy as possible to interact with the Google Drive and Sheets APIs via Python. It is especially designed

Chris Larabee 1 Oct 2, 2021
❄️ Don't waste your money paying for new tokens, once you have used your tokens, clean them up and resell them!

TokenCleaner Don't waste your money paying for new tokens, once you have used your tokens, clean them up and resell them! If you have a very large qua

0xVichy 59 Nov 14, 2022
A python script fetches all your starred repositories from your GitHub account and clones them to your server so you will never lose important resources

A python script fetches all your starred repositories from your GitHub account and clones them to your server so you will never lose important resources

Ringo Hoffmann 27 Oct 1, 2022
Ditch Xiaomi's cloud and use a Telegram bot instead

Yi-Home_Telegram_Bot_Interface Ditch Xiaomi's cloud and use a Telegram bot instead Features Motion detection Works by monitoring a tmp file that is cr

Erli 10 Aug 18, 2022
Analog clock that shows the weather instead of the actual numerical hour it points to.

Eli's weatherClock An digital analog clock but instead of showing the hours, the clock shows the weather at that hour of the day. So instead of showin

Kovin 154 Dec 1, 2022
A Telegram Music Tag Editor Bot that can remove almost all usernames in the music tags and add own username instead.

Music Tag Editor Bot A Telegram Music Tag Editor Bot that can remove almost all usernames in the music tags and add own username instead. It can also

null 14 Oct 21, 2022
A bot can play all variants, but standard are abit weak, so if you need strongest you can change fsf instead of stockfish_14_Dev

MAINTAINERS Drdisrespect1 and drrespectable lichess-bot Engine communication code taken from https://github.com/ShailChoksi/lichess-bot by ShailChoksi

RPNS Nimsilu 1 Dec 12, 2021
Prisma Cloud utility scripts, and a Python SDK for Prisma Cloud APIs.

pcs-toolbox Prisma Cloud utility scripts, and a Python SDK for Prisma Cloud APIs. Table of Contents Support Setup Configuration Script Usage CSPM Scri

Palo Alto Networks 34 Dec 15, 2022
Bot that embeds a random hysterical meme from Reddit into your text channel as an embedded message, using an API call.

Discord_Meme_Bot ?? Bot that embeds a random hysterical meme from Reddit into your text channel as an embedded message, using an API call. Add the bot

null 2 Jan 16, 2022
A Pancakeswap v2 trading client (and bot) with limit orders, stop-loss, custom gas strategies, a GUI and much more.

Pancakeswap v2 trading client A Pancakeswap trading client (and bot) with limit orders, stop-loss, custom gas strategies, a GUI and much more. If you

null 571 Mar 15, 2022
A Pancakeswap and Uniswap trading client (and bot) with limit orders, marker orders, stop-loss, custom gas strategies, a GUI and much more.

Pancakeswap and Uniswap trading client Adam A A Pancakeswap and Uniswap trading client (and bot) with market orders, limit orders, stop-loss, custom g

null 570 Mar 9, 2022
Andrei 1.4k Dec 24, 2022
Discord bot code to stop users that are scamming with fake messages of free discord nitro on servers in order to steal users accounts.

AntiScam Discord bot code to stop users that are scamming with fake messages of free discord nitro on servers in order to steal users accounts. How to

H3cJP 94 Dec 15, 2022
Cedric Owens 16 Sep 27, 2022
A Telegram Repo For Devs To Controll The Bots Under Maintenance.This Bot Is For Developers, If Your Bot Is Down, Use This Repo To Give Your Dear Subscribers Some Support By Providing Them Response.

Maintenance Bot A Telegram Repo For Devs To Controll The Bots Under Maintenance About This Bot This Bot Is For Developers, If Your Bot Is Down, Use Th

Vɪᴠᴇᴋ 47 Dec 29, 2022
A Telegram Repo For Devs To Controll The Bots Under Maintenance.This Bot Is For Developers, If Your Bot Is Down, Use This Repo To Give Your Dear Subscribers Some Support By Providing Them Response.

Maintenance Bot A Telegram Repo For Devs To Controll The Bots Under Maintenance About This Bot This Bot Is For Developers, If Your Bot Is Down, Use Th

Vɪᴠᴇᴋ 47 Dec 29, 2022
GitPython is a python library used to interact with Git repositories.

Gitoxide: A peek into the future… I started working on GitPython in 2009, back in the days when Python was 'my thing' and I had great plans with it. O

null 3.8k Jan 3, 2023
Python library to interact with a Z-Wave JS server.

zwave-js-server-python Python library for communicating with zwave-js-server. Goal for this library is to replicate the structure and the events of Z-

Home Assistant Libraries 54 Dec 18, 2022
Revolt.py - An async library to interact with the https://revolt.chat api.

Revolt.py An async library to interact with the https://revolt.chat api. This library will be focused on making bots and i will not implement anything

Zomatree 0 Oct 8, 2022