Skip to content

Commit

Permalink
Merge pull request #91 from NASA-AMMOS/develop
Browse files Browse the repository at this point in the history
Aerie-CLI 2.1.0
  • Loading branch information
cartermak authored Oct 20, 2023
2 parents 43ee527 + 6d6ee06 commit 6343bd9
Show file tree
Hide file tree
Showing 18 changed files with 318 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DOCKER_TAG=v1.12.0
DOCKER_TAG=v1.14.0
REPOSITORY_DOCKER_URL=ghcr.io/nasa-ammos

AERIE_USERNAME=aerie
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ env:
HASURA_GRAPHQL_JWT_SECRET: "${{secrets.HASURA_GRAPHQL_JWT_SECRET}}"
POSTGRES_USER: "${{secrets.POSTGRES_USER}}"
POSTGRES_PASSWORD: "${{secrets.POSTGRES_PASSWORD}}"
DOCKER_TAG: "v1.12.0"
REPOSITORY_DOCKER_URL: "ghcr.io/nasa-ammos"

jobs:
Expand Down Expand Up @@ -48,6 +47,7 @@ jobs:
strategy:
matrix:
python-version: [ "3.6.15", "3.11" ]
aerie-version: ["1.13.0", "1.14.0"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -59,7 +59,9 @@ jobs:
python -m pip install --upgrade pip
pip install pytest
pip install .
- name: Set up aerie instance
- name: Set up instance of Aerie ${{ matrix.aerie-version }}
env:
DOCKER_TAG: v${{ matrix.aerie-version }} # Prefix 'v' used in Aerie Docker image tags
run: |
docker compose -f docker-compose-test.yml up -d
docker images
Expand All @@ -68,6 +70,8 @@ jobs:
run: sleep 60s
shell: bash
- name: Run integration tests
env:
AERIE_VERSION: ${{ matrix.aerie-version }}
run: |
cd tests
pytest integration_tests
Expand Down
94 changes: 90 additions & 4 deletions src/aerie_cli/aerie_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1630,15 +1630,29 @@ def get_constraint_by_id(self, id):

def get_constraint_violations(self, plan_id):
get_violations_query = """
query ($plan_id: Int!){
constraintViolations(planId: $plan_id) {
violations
query ($plan_id: Int!) {
constraintResults: constraintViolations(planId: $plan_id) {
constraintId
constraintName
type
resourceIds
violations {
activityInstanceIds
windows {
start
end
}
}
gaps {
start
end
}
}
}
"""

resp = self.aerie_host.post_to_graphql(get_violations_query, plan_id=plan_id)
return resp["violations"]
return resp

def get_resource_types(self, model_id: int) -> List[ResourceType]:
"""Get resource types (value schema)
Expand Down Expand Up @@ -1761,3 +1775,75 @@ def delete_directive_metadata_schema(self, key) -> list:
key=key
)
return resp["key"]

def list_plan_collaborators(self, plan_id: int) -> list:
"""List plan collaborators
Args:
plan_id (int): ID of Plan to list collaborators of
Returns:
list[str]: List of collaborator usernames
"""
query = """
query GetPlanCollaborators($plan_id: Int!) {
plan_by_pk(id: $plan_id) {
collaborators {
collaborator
}
}
}
"""

resp = self.aerie_host.post_to_graphql(
query,
plan_id=plan_id
)
return [c["collaborator"] for c in resp["collaborators"]]

def add_plan_collaborator(self, plan_id: int, user: str):
"""Add a plan collaborator
Args:
plan_id (int): ID of plan to add collaborator to
user (str): Username of collaborator
"""
query = """
mutation addPlanCollaborator($plan_id: Int!, $collaborator: String!) {
insert_plan_collaborators_one(object: {plan_id: $plan_id, collaborator: $collaborator}) {
collaborator
}
}
"""

self.aerie_host.post_to_graphql(
query,
plan_id=plan_id,
collaborator=user
)

def delete_plan_collaborator(self, plan_id: int, user: str):
"""Delete a plan collaborator
Args:
plan_id (int): ID of the plan to delete a collaborator from
user (str): Username of the collaborator
"""

query = """
mutation DeletePlanCollaborator($plan_id: Int!, $collaborator: String!) {
delete_plan_collaborators_by_pk(collaborator: $collaborator, plan_id: $plan_id) {
collaborator
plan_id
}
}
"""

resp = self.aerie_host.post_to_graphql(
query,
plan_id=plan_id,
collaborator=user
)

if resp is None:
raise RuntimeError(f"Failed to delete plan collaborator")
23 changes: 9 additions & 14 deletions src/aerie_cli/aerie_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ def __init__(self, encoded_jwt: str) -> None:
encoded_jwt_payload = b64decode(jwt_components[1] + "==", validate=False)
try:
payload = json.loads(encoded_jwt_payload)
self.active_role = payload["activeRole"]
self.allowed_roles = payload["https://hasura.io/jwt/claims"][
"x-hasura-allowed-roles"
]
self.default_role = payload["https://hasura.io/jwt/claims"]["x-hasura-default-role"]
self.username = payload["username"]

except KeyError:
Expand Down Expand Up @@ -83,6 +83,7 @@ def __init__(
self.gateway_url = gateway_url
self.configuration_name = configuration_name
self.aerie_jwt = None
self.active_role = None

def post_to_graphql(self, query: str, **kwargs) -> Dict:
"""Issue a post request to the Aerie instance GraphQL API
Expand Down Expand Up @@ -201,17 +202,7 @@ def change_role(self, new_role: str) -> None:
f"Cannot set role {new_role}. Must be one of: {', '.join(self.aerie_jwt.allowed_roles)}"
)

resp = self.session.post(
self.gateway_url + "/auth/changeRole",
json={"role": new_role},
headers=self.get_auth_headers(),
)

try:
resp_json = process_gateway_response(resp)
self.aerie_jwt = AerieJWT(resp_json["token"])
except (RuntimeError, KeyError):
raise RuntimeError(f"Failed to select new role")
self.active_role = new_role

def check_auth(self) -> bool:
"""Checks if session is correctly authenticated with Aerie host
Expand All @@ -237,9 +228,12 @@ def check_auth(self) -> bool:
return False

def get_auth_headers(self):
if self.aerie_jwt is None:
return {}

return {
"Authorization": f"Bearer {self.aerie_jwt.encoded_jwt}",
"x-hasura-role": self.aerie_jwt.active_role,
"x-hasura-role": self.active_role,
}

def is_auth_enabled(self) -> bool:
Expand All @@ -248,7 +242,7 @@ def is_auth_enabled(self) -> bool:
Returns:
bool: False if authentication is disabled, otherwise True
"""
resp = self.session.get(self.gateway_url + "/auth/user")
resp = self.session.get(self.gateway_url + "/auth/session")
if resp.ok:
try:
resp_json = resp.json()
Expand All @@ -275,6 +269,7 @@ def authenticate(self, username: str, password: str = None):
raise RuntimeError("Failed to authenticate")

self.aerie_jwt = AerieJWT(resp_json["token"])
self.active_role = self.aerie_jwt.default_role

if not self.check_auth():
raise RuntimeError(f"Failed to open session")
Expand Down
13 changes: 8 additions & 5 deletions src/aerie_cli/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from aerie_cli.utils.configurations import find_configuration

app = typer.Typer()
app.add_typer(plans.app, name="plans")
app.add_typer(plans.plans_app, name="plans")
app.add_typer(models.app, name="models")
app.add_typer(configurations.app, name="configurations")
app.add_typer(expansion.app, name="expansion")
Expand Down Expand Up @@ -85,6 +85,9 @@ def activate_session(
name: str = typer.Option(
None, "--name", "-n", help="Name for this configuration", metavar="NAME"
),
username: str = typer.Option(
None, "--username", "-u", help="Specify/override configured Aerie username", metavar="USERNAME"
),
role: str = typer.Option(
None, "--role", "-r", help="Specify a non-default role", metavar="ROLE"
)
Expand All @@ -99,7 +102,7 @@ def activate_session(

conf = PersistentConfigurationManager.get_configuration_by_name(name)

session = start_session_from_configuration(conf)
session = start_session_from_configuration(conf, username)

if role is not None:
if role in session.aerie_jwt.allowed_roles:
Expand Down Expand Up @@ -134,14 +137,14 @@ def change_role(
client = get_active_session_client()

if role is None:
typer.echo(f"Active Role: {client.aerie_host.aerie_jwt.active_role}")
typer.echo(f"Active Role: {client.aerie_host.active_role}")
role = select_from_list(client.aerie_host.aerie_jwt.allowed_roles)

client.aerie_host.change_role(role)

PersistentSessionManager.set_active_session(client.aerie_host)

typer.echo(f"Changed role to: {client.aerie_host.aerie_jwt.active_role}")
typer.echo(f"Changed role to: {client.aerie_host.active_role}")


@app.command("status")
Expand All @@ -155,4 +158,4 @@ def print_status():
if client.aerie_host.configuration_name:
typer.echo(f"Active configuration: {client.aerie_host.configuration_name}")

typer.echo(f"Active role: {client.aerie_host.aerie_jwt.active_role}")
typer.echo(f"Active role: {client.aerie_host.active_role}")
Loading

0 comments on commit 6343bd9

Please sign in to comment.