diff --git a/CHANGELOG.md b/CHANGELOG.md index 88746e0..3d59c27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ -# Change Log for npg_porch Project + +# Change Log for npg_porch_cli Project The format is based on [Keep a Changelog](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +* Implemented the `create_token` action. Provided the caller has an admin token, + this action generates and returns a new pipeline-specific token. + ## [0.1.0] - 2024-07-23 ### Added -# Initial project scaffold, code and tests +* Initial project scaffold, code and tests diff --git a/src/npg_porch_cli/api.py b/src/npg_porch_cli/api.py index 11f6dba..e639cc1 100644 --- a/src/npg_porch_cli/api.py +++ b/src/npg_porch_cli/api.py @@ -162,7 +162,9 @@ def list_client_actions() -> list[str]: return sorted(_PORCH_CLIENT_ACTIONS.keys()) -def send(action: PorchAction, pipeline: Pipeline = None) -> dict | list: +def send( + action: PorchAction, pipeline: Pipeline = None, description: str | None = None +) -> dict | list: """Sends a request to the porch API server. Sends a request to the porch API server to perform an action defined @@ -174,6 +176,8 @@ def send(action: PorchAction, pipeline: Pipeline = None) -> dict | list: npg_porch_cli.api.PorchAction object pipeline: npg_porch_cli.api.Pipeline object + description: + A description for the new token, optional Returns: The server's response is returned as a Python data structure. @@ -183,6 +187,8 @@ def send(action: PorchAction, pipeline: Pipeline = None) -> dict | list: function = _PORCH_CLIENT_ACTIONS[action.action] if action.action == "list_pipelines": return function(action=action) + elif action.action == "create_token": + return function(action=action, pipeline=pipeline, description=description) return function(action=action, pipeline=pipeline) @@ -333,6 +339,31 @@ def update_task(action: PorchAction, pipeline: Pipeline): ) +def create_token(action: PorchAction, pipeline: Pipeline, description: str): + """Creates a new token for the pipeline. + + Args: + action: + npg_porch_cli.api.PorchAction object + pipeline: + npg_porch_cli.api.Pipeline object + description: + A short token description + + Returns: + A dictionary containing a new token. + """ + + if not description: + raise TypeError("Token description should be given") + + return send_request( + validate_ca_cert=action.validate_ca_cert, + url=urljoin(action.porch_url, f"pipelines/{pipeline.name}/token/{description}"), + method="POST", + ) + + _PORCH_CLIENT_ACTIONS = { "list_tasks": list_tasks, "list_pipelines": list_pipelines, @@ -340,6 +371,7 @@ def update_task(action: PorchAction, pipeline: Pipeline): "add_task": add_task, "claim_task": claim_task, "update_task": update_task, + "create_token": create_token, } diff --git a/src/npg_porch_cli/api_cli_user.py b/src/npg_porch_cli/api_cli_user.py index cd94ddc..3fcdd7a 100755 --- a/src/npg_porch_cli/api_cli_user.py +++ b/src/npg_porch_cli/api_cli_user.py @@ -43,6 +43,7 @@ def run(): list_tasks list_pipelines add_pipeline + create_token add_task claim_task update_task @@ -54,13 +55,15 @@ def run(): `--pipeline_name` is defined, `list_tasks` returns a list of tasks for this pipeline, otherwise all registered tasks are returned. - All non-list actions require all `--pipeline`, `pipeline_url` and + All non-list actions require `--pipeline`, `pipeline_url` and `--pipeline_version` defined. The `add_task` and `update_task` actions require the `--task_json` to be defined. In addition to this, for the `update_task` action `--status` should be defined. + The `create_token` action requires that the `--description` is defined. + NPG_PORCH_TOKEN environment variable should be set to the value of either an admin or project-specific token. @@ -94,6 +97,7 @@ def run(): parser.add_argument("--pipeline", type=str, help="Pipeline name, optional") parser.add_argument("--task_json", type=str, help="Task as JSON, optional") parser.add_argument("--status", type=str, help="New status to set, optional") + parser.add_argument("--description", type=str, help="Token description, optional") args = parser.parse_args() @@ -110,4 +114,9 @@ def run(): name=args.pipeline, uri=args.pipeline_url, version=args.pipeline_version ) - print(json.dumps(send(action=action, pipeline=pipeline), indent=2)) + print( + json.dumps( + send(action=action, pipeline=pipeline, description=args.description), + indent=2, + ) + ) diff --git a/tests/test_api.py b/tests/test_api.py index 685b47b..9de7f7f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -51,6 +51,7 @@ def test_listing_actions(): "add_pipeline", "add_task", "claim_task", + "create_token", "list_pipelines", "list_tasks", "update_task", @@ -76,8 +77,8 @@ def test_porch_action_class(monkeypatch): PorchAction(porch_url=url, action="list_tools") assert ( e.value.args[0] == "Action 'list_tools' is not valid. " - "Valid actions: add_pipeline, add_task, claim_task, list_pipelines, " - "list_tasks, update_task" + "Valid actions: add_pipeline, add_task, claim_task, create_token, " + "list_pipelines, list_tasks, update_task" ) pa = PorchAction(porch_url=url, action="list_tasks") @@ -247,3 +248,29 @@ def mock_get_200(*args, **kwargs): porch_url=url, action="update_task", task_input=task, task_status="DONE" ) assert send(action=pa, pipeline=p) == response_data + + with monkeypatch.context() as mkp: + response_data = { + "name": "p1", + "description": "for my pipeline", + "token": "ccceddd450aaa", + } + + def mock_get_200(*args, **kwargs): + return MockPorchResponse(response_data, 200) + + mkp.setattr(requests, "request", mock_get_200) + + pa = PorchAction(porch_url=url, action="create_token") + + error_message = "Token description should be given" + with pytest.raises(TypeError) as e: + send(action=pa, pipeline=p) + assert e.value.args[0] == error_message + with pytest.raises(TypeError) as e: + send(action=pa, pipeline=p, description="") + assert e.value.args[0] == error_message + + assert ( + send(action=pa, pipeline=p, description="for my pipeline") == response_data + )