https://github.com/nasa-ammos/aerie-cli
An unofficial CLI for interacting with Aerie planning software
https://github.com/nasa-ammos/aerie-cli
Last synced: about 1 year ago
JSON representation
An unofficial CLI for interacting with Aerie planning software
- Host: GitHub
- URL: https://github.com/nasa-ammos/aerie-cli
- Owner: NASA-AMMOS
- License: mit
- Created: 2022-11-11T05:54:25.000Z (over 3 years ago)
- Default Branch: develop
- Last Pushed: 2025-01-31T19:13:22.000Z (over 1 year ago)
- Last Synced: 2025-04-03T16:35:27.968Z (about 1 year ago)
- Language: Python
- Size: 58.1 MB
- Stars: 3
- Watchers: 18
- Forks: 4
- Open Issues: 52
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Aerie-CLI
Aerie-CLI provides a command-line interface and user-extendable Python API for interacting with an instance of Aerie.
> Note: this project is an informal CLI and is _not_ maintained by the MPSA Aerie team.
## Getting Started
This short procedure will get you up and running with the basics of the CLI.
1. Install/update to Python >= 3.6
2. Install Aerie-CLI from Github:
```sh
python3 -m pip install git+https://github.com/NASA-AMMOS/aerie-cli.git@main
```
3. Configure access to an Aerie host
1. If you've been provided a Configuration JSON, reference that file
2. If you don't have already have a Configuration JSON, copy the following to a JSON file for a local Aerie deployment (replacing the username with your own):
```json
[
{
"name": "localhost",
"graphql_url": "http://localhost:8080/v1/graphql",
"gateway_url": "http://localhost:9000",
"username": "my_username"
}
]
```
3. Load either your given configuration(s) or the configuration above into Aerie-CLI:
```sh
aerie-cli configurations load -i JSON_FILE
```
4. Activate a configuration to start a session with an Aerie host:
```sh
➜ aerie-cli activate
1) localhost
Select an option: 1
```
5. Try out a command to list the plans in Aerie:
```sh
aerie-cli plans list
```
6. Use the `--help` flag on any command to see available subcommands and parameters. For example:
```sh
aerie-cli --help
...
aerie-cli plans --help
...
aerie-cli plans download --help
```
---
## CLI Usage
### Setup
Aerie-CLI uses configurations to define different Aerie hosts. Define configurations by either loading JSON configurations or manually via the CLI. Configurations persist on a per-user basis and may be shared between installations.
#### Defining Hosts with a Configuration File
If you have a file of configurations to load, you can use the `configurations load` command:
```sh
aerie-cli configurations load -i JSON_FILE
```
You can view the configurations you've loaded with the `configurations list` command:
```sh
➜ aerie-cli configurations list
Configuration file location: /Users//Library/Application Support/aerie_cli/config.json
Aerie Host Configurations
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Host Name ┃ GraphQL API URL ┃ Aerie Gateway URL ┃ Username ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ localhost │ http://localhost:8080/v1/graphql │ http://localhost:9000 │ │
└───────────┴──────────────────────────────────┴───────────────────────┴──────────┘
```
#### Defining hosts via the CLI
If you haven't been provided a JSON configuration for a host, you can create a configuration by running `aerie-cli configurations create` and follow the prompts.
#### Full Specification
Each configuration is stored as JSON object list entry in the configuration file provided with the `configurations list` command. The full contents of a host configuration are:
| Field | Description | Required |
| :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `name` | User-facing name of the host | Yes |
| `graphql_url` | URL of the Aerie instance's Hasura GraphQL API | Yes |
| `gateway_url` | URL of the Aerie instance's Gateway | Yes |
| `username` | Username for authentication with Aerie | No |
| `external_auth` | Specification for external authentication required to reach an Aerie instance. See [Configuring for External Authentication](#configuring-for-external-authentication) for details | No |
### Sessions and Roles
Aerie-CLI maintains a persistent "session" with an Aerie instance so multiple commands can run without needing to re-authenticate. To start a session, use the `activate` command:
```sh
➜ aerie-cli activate
1) localhost
...
Select an option: 1
```
Aerie uses "roles" to adjust what a client is permitted to do. To view the active configuration name and current role, use the `status` command:
```sh
➜ aerie-cli status
Active configuration: localhost
Active role: viewer
```
The default role is configured by Aerie. To change the selected role for the active Aerie-CLI session, use the `role` command:
```sh
➜ aerie-cli role
Active Role: viewer
1) aerie_admin
2) user
3) viewer
Select an option: 1
Changed role to: aerie_admin
```
At any time, the active session can be closed with the `deactivate` command.
### Commands
Commands are the main functions available via the CLI and are broken down into several levels. For example, the top-level `plans` command has sub-commands for `list`, `upload`, `simulate`, and more. From any command or sub-command, use the `--help` flag to learn about what commands are available or what arguments are required.
Help at script level:
```sh
aerie-cli --help
```
Help at command level:
```sh
aerie-cli plans --help
```
Help at sub-command level:
```sh
aerie-cli plans download --help
```
#### Interactive vs. Non-Interactive
If a command is invoked without the necessary arugments, interactive prompts are provided:
```sh
➜ aerie-cli plans download
Id: 42
Output: sample-output.json
```
Alternatively, arguments can be provided using flags:
```sh
➜ aerie-cli plans download --id 42 --output sample-output.json
```
### Advanced Topics
#### Configuring for External Authentication
Aerie-CLI configurations include a mechanism to authenticate against an external authentication service which may require additional credentials as cookies for accessing Aerie. Aerie-CLI will issue a post request with given JSON data to a provided authentication endpoint and persist any returned cookies in a browser-like manner for the remainder of the Aerie-CLI session.
An external authentication service is configured using the key `external_auth` in the JSON configuration file as follows:
```json
{
"name": "my_host",
"graphql_url": "https://hostname/v1/graphql",
"gateway_url": "https://hostname/gateway",
"username": "my_username",
"external_auth": {
"auth_url": "https://auth_service/route",
"static_post_vars": {
"username": "my_username"
},
"secret_post_vars": [
"password"
]
}
}
```
Here, `static_post_vars` is an object containing fixed values to include in the post request payload such as usernames and other persistent, non-sensitive fields. `secret_post_vars` is a list of keys for credentials which may be sensitive or time-varying. The user will be prompted to provide the "secret" values using hidden entry in the terminal when activating a session with external authentication.
In this example, the user would be prompted to enter a value for "password" and, assuming they enter "my_password", the post request JSON would include the following:
```json
{
"username": "my_username",
"password": "my_password"
}
```
#### Using a Hasura Admin Secret
In some cases, an admin secret may be used to permit otherwise prohibited requests through Hasura (the software behind the Aerie API). When running a command, the user may add the `--hasura-admin-secret` flag after the `aerie-cli` command to use these elevated privileges for the following command.
---
## Python API
### Quickstart Guide
Instead of using the CLI for interactive use cases, the underlying classes and methods behind Aerie-CLI can be invoked directly in Python scripts.
The key constructs are:
- `aerie_cli.aerie_host.AerieHost`: An abstraction for an Aerie Host, including methods for authentication and issuing requests to the Aerie API.
- `aerie_cli.aerie_client.AerieClient`: A class containing common requests and reusable logic to interact with data in Aerie.
The following example defines an `AerieHost` using the necessary URLs, authenticates with a command-line prompt for the user's password, and issues a simple request using one of the built-in requests.
```py
from aerie_cli.aerie_client import AerieClient
from aerie_cli.aerie_host import AerieHost
from getpass import getpass
# These URLs define the Aerie host
GRAPHQL_URL = "http://myhostname:8080/v1/graphql"
GATEWAY_URL = "http://myhostname:9000"
# User credentials. The password may be omitted on Aerie instances with authentication disabled
USERNAME = "myusername"
PASSWORD = getpass(prompt='Password: ')
# Define the Aerie host and provide user credentials
aerie_host = AerieHost(GRAPHQL_URL, GATEWAY_URL)
aerie_host.authenticate(USERNAME, PASSWORD)
# AerieClient takes in a host and returns an object to issue requests to that host
client = AerieClient(aerie_host)
# Simple example of a request to get an activity plan using the plan ID
plan = client.get_activity_plan_by_id(42)
print(plan.name)
```
Look through the available methods in the provided `AerieClient` class to find ones that suit your needs.
### Adding Methods
If you need to write a custom query, you can extend the `AerieClient` class and add your own method. Access the Aerie host using the `aerie_host` property. For example:
```py
# ...
class MyCustomAerieClient(AerieClient):
def get_plan_id_by_name(self, plan_name: str) -> int:
my_query = """
query GetPlanIdByName($plan_name: String!) {
plan(where: { name: { _eq: $plan_name } }) {
id
}
}
"""
# Pass variables for the query as keyword arguments
resp = self.aerie_host.post_to_graphql(
my_query,
plan_name=plan_name
)
return resp[0]["id"]
```
Now, you can use your custom method like any other:
```py
# ...
client = MyCustomAerieClient(aerie_host)
plan_id = client.get_plan_id_by_name("my-plan-name")
print(plan_id)
```
### Using the Active CLI Session
If your application will be run by a user who may also be using the CLI, you may reduce the amount of code required to configure an Aerie host and instead just use the active session. Aerie-CLI provides a utility to retrieve an `AerieClient` instance from the active CLI session:
```py
from aerie_cli.utils.sessions import get_active_session_client
# client is an instance of `AerieClient`
client = get_active_session_client()
# Issue requests like normal
plan = client.get_activity_plan_by_id(...)
```
### Advanced Authentication
If you have needs for authentication (e.g., a custom token system) that aren't provided by Aerie-CLI, you can use any features supported by the [Python `requests`](https://requests.readthedocs.io/en/latest/) module's [`Session` class](https://requests.readthedocs.io/en/latest/api/#request-sessions). Instantiate a session object, manipulate/add headers/cookies/SSL certificates/etc. as necessary, and use to instantiate an `AerieHostSession`:
```py
# ...
from requests import Session
my_custom_requests_session = Session()
# Manipulate as necessary
# ...
aerie_host = AerieHost(
GRAPHQL_URL,
GATEWAY_URL,
session=my_custom_requests_session
)
aerie_host.authenticate(...)
client = AerieClient(aerie_host)
# Use client as normal
```
---
## Contributing
### Contributor Installation
The recommended develper installation method is in a [virutual environment](https://docs.python.org/3/library/venv.html) with an [editatable install via Pip](https://pip.pypa.io/en/latest/topics/local-project-installs/#editable-installs).
See this [primer](https://realpython.com/python-virtual-environments-a-primer/) or virtual environments to get set up. A quick and easy method is using Python's native `venv` package from the root of the Aerie-CLI repo:
```sh
python3 -m venv venv
source venv/bin/activate
```
Then, install Aerie-CLI in editable mode via Pip:
```sh
python3 -m pip install -e .
```
Now, your installation of Aerie-CLI will update as you make changes to the source code.
### Dependency Management
Dependencies are currently managed via Poetry. For more information on dependency and project management, see the [`poetry` docs](https://python-poetry.org/docs/master/).
### Testing
Aerie-CLI has unit tests and integration tests built with the [pytest](https://docs.pytest.org/) library.
#### Unit Tests
Unit tests can be run anytime and reference local test files. `test_aerie_client.py` is where unit tests are added to exercise particular methods of the `AerieClient` class using mocked Aerie API responses.
Run the unit tests using `pytest` from the `tests/unit_tests` directory:
```
cd tests
python3 -m pytest .
```
#### Integration Tests
A separate suite of tests is designed to validate CLI functionality against a local instance of Aerie. See the [integration testing documentation](tests/integration_tests/README.md) for details.
The integration tests are based on `Typer` testing documentation found [here](https://typer.tiangolo.com/tutorial/testing/).
### Releases
Aerie-CLI generally follows the [gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) workflow model for managing releases:
- Features are merged to the `develop` branch.
- Release branches are made from develop and merge in `main` for testing.
- Releases are made from the tip of the `main` branch.
- Hotfix branches can be made from `main` to resolve bugs without needing to incorporate new features.
Version numbers are managed using [semantic versioning](https://semver.org) in [`pyproject.toml`](pyproject.toml):
- The version tag on `develop` is fixed at `0.0.0-dev0`
- Release versions are incremented on release branches before being merged to main.
The full workflow for releasing a new version of Aerie-CLI:
- Make a release branch from `develop`
- Merge `main` into the release branch
- Commit a version increment in `pyproject.toml`
- Open a PR to merge the release branch into `main` and verify CI passes
- Merge the PR and tag a release on `main`