@orcabus/platform-cdk-constructs 0.0.7-alpha.6 → 0.0.7-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +173 -39
- package/api-gateway/api-gateway.js +1 -1
- package/deployment-stack-pipeline/pipeline.d.ts +14 -1
- package/deployment-stack-pipeline/pipeline.js +24 -6
- package/dynamodb/index.js +2 -2
- package/ecs/index.js +1 -1
- package/lambda/config.d.ts +1 -0
- package/lambda/config.js +7 -2
- package/lambda/index.d.ts +25 -0
- package/lambda/index.js +83 -4
- package/lambda/layers/fastapi_tools/poetry.lock +1339 -0
- package/lambda/layers/fastapi_tools/pyproject.toml +32 -0
- package/lambda/layers/fastapi_tools/src/fastapi_tools/__init__.py +20 -0
- package/lambda/layers/fastapi_tools/src/fastapi_tools/globals.py +3 -0
- package/lambda/layers/fastapi_tools/src/fastapi_tools/pagination_helpers.py +102 -0
- package/lambda/layers/icav2_tools/poetry.lock +85 -0
- package/lambda/layers/icav2_tools/pyproject.toml +26 -0
- package/lambda/layers/icav2_tools/src/icav2_tools/__init__.py +19 -0
- package/lambda/layers/icav2_tools/src/icav2_tools/aws_helpers.py +116 -0
- package/lambda/layers/icav2_tools/src/icav2_tools/globals.py +2 -0
- package/lambda/layers/orcabus_api_tools/src/orcabus_api_tools/icav2_wes/__init__.py +79 -0
- package/lambda/layers/orcabus_api_tools/src/orcabus_api_tools/icav2_wes/create_helpers.py +29 -0
- package/lambda/layers/orcabus_api_tools/src/orcabus_api_tools/icav2_wes/globals.py +6 -0
- package/lambda/layers/orcabus_api_tools/src/orcabus_api_tools/icav2_wes/models.py +50 -0
- package/lambda/layers/orcabus_api_tools/src/orcabus_api_tools/icav2_wes/query_helpers.py +20 -0
- package/lambda/layers/orcabus_api_tools/src/orcabus_api_tools/icav2_wes/update_helpers.py +23 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=1.0.0"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "fastapi_tools"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A collection of secondary-level functions to use to interact with the OrcaBus"
|
|
9
|
+
license = "GPL-3.0-or-later"
|
|
10
|
+
authors = [
|
|
11
|
+
"Alexis Lucattini"
|
|
12
|
+
]
|
|
13
|
+
homepage = "https://github.com/orcabus/platform-cdk-constructs"
|
|
14
|
+
repository = "https://github.com/orcabus/platform-cdk-constructs"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.dependencies]
|
|
17
|
+
python = "^3.12, <3.13"
|
|
18
|
+
fastapi = {extras = ["standard"], version = ">=0.115.6"}
|
|
19
|
+
pydantic = "^2.10.5"
|
|
20
|
+
mangum = "^0.19.0"
|
|
21
|
+
dyntastic = "^0.16.0"
|
|
22
|
+
ulid-py = "^1.1.0"
|
|
23
|
+
requests = "^2.32.3"
|
|
24
|
+
|
|
25
|
+
[tool.poetry.group.dev]
|
|
26
|
+
optional = true
|
|
27
|
+
|
|
28
|
+
[tool.poetry.group.dev.dependencies]
|
|
29
|
+
pytest = "^7.0.0" # For testing only
|
|
30
|
+
# For typehinting only, not required at runtime
|
|
31
|
+
mypy-boto3-ssm = "^1.34"
|
|
32
|
+
mypy-boto3-secretsmanager = "^1.34"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .pagination_helpers import (
|
|
8
|
+
Links,
|
|
9
|
+
QueryPagination,
|
|
10
|
+
ResponsePagination,
|
|
11
|
+
QueryPaginatedResponse,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
# Pagination helpers
|
|
16
|
+
"Links",
|
|
17
|
+
"QueryPagination",
|
|
18
|
+
"ResponsePagination",
|
|
19
|
+
"QueryPaginatedResponse",
|
|
20
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from typing import Optional, TypedDict, List, Any, ClassVar, Dict, Self
|
|
3
|
+
from urllib.parse import urlparse, urlunparse
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from .globals import DEFAULT_ROWS_PER_PAGE
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Links(TypedDict):
|
|
11
|
+
previous: Optional[str]
|
|
12
|
+
next: Optional[str]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class QueryPagination(TypedDict):
|
|
16
|
+
page: Optional[int]
|
|
17
|
+
rowsPerPage: Optional[int]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ResponsePagination(QueryPagination):
|
|
21
|
+
count: int
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class QueryPaginatedResponse(BaseModel):
|
|
25
|
+
"""
|
|
26
|
+
Job Query Response, includes a list of jobs, the total
|
|
27
|
+
"""
|
|
28
|
+
links: Links
|
|
29
|
+
pagination: ResponsePagination
|
|
30
|
+
results: List[Any]
|
|
31
|
+
# Implemented in subclass
|
|
32
|
+
url_placeholder: ClassVar[str] = None
|
|
33
|
+
|
|
34
|
+
def resolve_url_placeholder(self, **kwargs) -> str:
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_results_list(cls, results: List[Any], query_pagination: QueryPagination, params_response: Dict, **kwargs) -> Self:
|
|
39
|
+
# From pagination calculate the links
|
|
40
|
+
if cls.url_placeholder is None:
|
|
41
|
+
raise ValueError("URL must be set for QueryPaginatedResponse")
|
|
42
|
+
url_obj = urlparse(cls.resolve_url_placeholder(**kwargs))
|
|
43
|
+
|
|
44
|
+
query_pagination = {
|
|
45
|
+
'page': query_pagination.get('page', 1),
|
|
46
|
+
'rowsPerPage': query_pagination.get('rowsPerPage', DEFAULT_ROWS_PER_PAGE)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
response_pagination: ResponsePagination = ResponsePagination(
|
|
50
|
+
**dict(
|
|
51
|
+
**query_pagination,
|
|
52
|
+
# Add in count using the results length
|
|
53
|
+
**{
|
|
54
|
+
'count': len(results)
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if query_pagination['page'] == 1:
|
|
60
|
+
previous_page = None
|
|
61
|
+
else:
|
|
62
|
+
params_response_prev = params_response.copy()
|
|
63
|
+
params_response_prev['page'] = query_pagination['page'] - 1
|
|
64
|
+
params_str = "&".join([
|
|
65
|
+
f"{k}={v}"
|
|
66
|
+
for k, v in params_response_prev.items()
|
|
67
|
+
])
|
|
68
|
+
|
|
69
|
+
previous_page = str(urlunparse(
|
|
70
|
+
(url_obj.scheme, url_obj.netloc, url_obj.path, None, params_str, None)
|
|
71
|
+
))
|
|
72
|
+
|
|
73
|
+
if ( response_pagination['page'] * response_pagination['rowsPerPage'] ) >= response_pagination['count']:
|
|
74
|
+
next_page = None
|
|
75
|
+
else:
|
|
76
|
+
params_response_next = params_response.copy()
|
|
77
|
+
params_response_next['page'] = query_pagination['page'] + 1
|
|
78
|
+
params_str = "&".join([
|
|
79
|
+
f"{k}={v}"
|
|
80
|
+
for k, v in params_response_next.items()
|
|
81
|
+
])
|
|
82
|
+
next_page = str(urlunparse(
|
|
83
|
+
(url_obj.scheme, url_obj.netloc, url_obj.path, None, params_str, None)
|
|
84
|
+
))
|
|
85
|
+
|
|
86
|
+
# Calculate the start and end of the results
|
|
87
|
+
results_start = ( query_pagination['page'] - 1 ) * query_pagination['rowsPerPage']
|
|
88
|
+
results_end = results_start + query_pagination['rowsPerPage']
|
|
89
|
+
|
|
90
|
+
# Generate the response object
|
|
91
|
+
return cls(
|
|
92
|
+
links={
|
|
93
|
+
'previous': previous_page,
|
|
94
|
+
'next': next_page
|
|
95
|
+
},
|
|
96
|
+
pagination=response_pagination,
|
|
97
|
+
results=results[results_start:results_end]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if typing.TYPE_CHECKING:
|
|
101
|
+
def model_dump(self, **kwargs) -> 'Self':
|
|
102
|
+
pass
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
|
2
|
+
|
|
3
|
+
[[package]]
|
|
4
|
+
name = "colorama"
|
|
5
|
+
version = "0.4.6"
|
|
6
|
+
description = "Cross-platform colored terminal text."
|
|
7
|
+
optional = false
|
|
8
|
+
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
|
9
|
+
files = [
|
|
10
|
+
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
|
11
|
+
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "iniconfig"
|
|
16
|
+
version = "2.1.0"
|
|
17
|
+
description = "brain-dead simple config-ini parsing"
|
|
18
|
+
optional = false
|
|
19
|
+
python-versions = ">=3.8"
|
|
20
|
+
files = [
|
|
21
|
+
{file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
|
|
22
|
+
{file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[[package]]
|
|
26
|
+
name = "mypy-boto3-secretsmanager"
|
|
27
|
+
version = "1.38.0"
|
|
28
|
+
description = "Type annotations for boto3 SecretsManager 1.38.0 service generated with mypy-boto3-builder 8.10.1"
|
|
29
|
+
optional = false
|
|
30
|
+
python-versions = ">=3.8"
|
|
31
|
+
files = [
|
|
32
|
+
{file = "mypy_boto3_secretsmanager-1.38.0-py3-none-any.whl", hash = "sha256:48d5057450ee307b132ce2d0976233a2c5331616fabdf423ecbc103f7431dd5e"},
|
|
33
|
+
{file = "mypy_boto3_secretsmanager-1.38.0.tar.gz", hash = "sha256:1666108e70f03e4dc1de449388d7facb77aba231a026bac0c3240fc27fd31a98"},
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[[package]]
|
|
37
|
+
name = "packaging"
|
|
38
|
+
version = "25.0"
|
|
39
|
+
description = "Core utilities for Python packages"
|
|
40
|
+
optional = false
|
|
41
|
+
python-versions = ">=3.8"
|
|
42
|
+
files = [
|
|
43
|
+
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
|
|
44
|
+
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[[package]]
|
|
48
|
+
name = "pluggy"
|
|
49
|
+
version = "1.6.0"
|
|
50
|
+
description = "plugin and hook calling mechanisms for python"
|
|
51
|
+
optional = false
|
|
52
|
+
python-versions = ">=3.9"
|
|
53
|
+
files = [
|
|
54
|
+
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
|
|
55
|
+
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[package.extras]
|
|
59
|
+
dev = ["pre-commit", "tox"]
|
|
60
|
+
testing = ["coverage", "pytest", "pytest-benchmark"]
|
|
61
|
+
|
|
62
|
+
[[package]]
|
|
63
|
+
name = "pytest"
|
|
64
|
+
version = "7.4.4"
|
|
65
|
+
description = "pytest: simple powerful testing with Python"
|
|
66
|
+
optional = false
|
|
67
|
+
python-versions = ">=3.7"
|
|
68
|
+
files = [
|
|
69
|
+
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
|
70
|
+
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[package.dependencies]
|
|
74
|
+
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
|
75
|
+
iniconfig = "*"
|
|
76
|
+
packaging = "*"
|
|
77
|
+
pluggy = ">=0.12,<2.0"
|
|
78
|
+
|
|
79
|
+
[package.extras]
|
|
80
|
+
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
|
81
|
+
|
|
82
|
+
[metadata]
|
|
83
|
+
lock-version = "2.0"
|
|
84
|
+
python-versions = "^3.12, <3.13"
|
|
85
|
+
content-hash = "6d7f1b2b62e2f7ec2963e9cdc6621e96b3e6477b70c155a989cf09368378ae80"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=1.0.0"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "icav2_tools"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Useful layer for python lambdas that use ICAv2, smart-collection of the ICAv2 access token"
|
|
9
|
+
license = "GPL-3.0-or-later"
|
|
10
|
+
authors = [
|
|
11
|
+
"Alexis Lucattini"
|
|
12
|
+
]
|
|
13
|
+
homepage = "https://github.com/orcabus/platform-cdk-constructs"
|
|
14
|
+
repository = "https://github.com/orcabus/platform-cdk-constructs"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.dependencies]
|
|
17
|
+
python = "^3.12, <3.13"
|
|
18
|
+
wrapica = "2.27.1.post20240830140737"
|
|
19
|
+
|
|
20
|
+
[tool.poetry.group.dev]
|
|
21
|
+
optional = true
|
|
22
|
+
|
|
23
|
+
[tool.poetry.group.dev.dependencies]
|
|
24
|
+
pytest = "^7.0.0" # For testing only
|
|
25
|
+
# For typehinting only, not required at runtime
|
|
26
|
+
mypy-boto3-secretsmanager = "^1.34"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from .aws_helpers import get_icav2_access_token
|
|
4
|
+
from os import environ
|
|
5
|
+
from .globals import ICAV2_BASE_URL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def set_icav2_env_vars():
|
|
9
|
+
"""
|
|
10
|
+
Set the environment variables for the ICAV2 API
|
|
11
|
+
:return:
|
|
12
|
+
"""
|
|
13
|
+
environ["ICAV2_ACCESS_TOKEN"] = get_icav2_access_token()
|
|
14
|
+
environ["ICAV2_BASE_URL"] = ICAV2_BASE_URL
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"set_icav2_env_vars"
|
|
19
|
+
]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Standard imports
|
|
4
|
+
import typing
|
|
5
|
+
from typing import Optional
|
|
6
|
+
import boto3
|
|
7
|
+
import json
|
|
8
|
+
from os import environ
|
|
9
|
+
import urllib3
|
|
10
|
+
from urllib.parse import urlunparse
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Type hinting
|
|
14
|
+
if typing.TYPE_CHECKING:
|
|
15
|
+
from mypy_boto3_secretsmanager import SecretsManagerClient
|
|
16
|
+
from mypy_boto3_ssm import SSMClient
|
|
17
|
+
|
|
18
|
+
# Set globals
|
|
19
|
+
from .globals import ICAV2_BASE_URL
|
|
20
|
+
|
|
21
|
+
http = urllib3.PoolManager()
|
|
22
|
+
|
|
23
|
+
LOCAL_HTTP_CACHE_PORT = 2773
|
|
24
|
+
PARAMETER_URL = '/systemsmanager/parameters/get/'
|
|
25
|
+
SECRETS_URL = '/secretsmanager/get/'
|
|
26
|
+
|
|
27
|
+
def retrieve_extension_value(url, query):
|
|
28
|
+
url = str(urlunparse((
|
|
29
|
+
'http', f'localhost:{LOCAL_HTTP_CACHE_PORT}',
|
|
30
|
+
url, None,
|
|
31
|
+
"&".join(list(map(
|
|
32
|
+
lambda kv: f"{kv[0]}={kv[1]}",
|
|
33
|
+
query.items()
|
|
34
|
+
))), None
|
|
35
|
+
)))
|
|
36
|
+
headers = {
|
|
37
|
+
"X-Aws-Parameters-Secrets-Token": environ.get('AWS_SESSION_TOKEN')
|
|
38
|
+
}
|
|
39
|
+
response = http.request("GET", url, headers=headers)
|
|
40
|
+
response = json.loads(response.data)
|
|
41
|
+
return response
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_ssm_value_from_cache(parameter_name: str) -> Optional[str]:
|
|
45
|
+
try:
|
|
46
|
+
return retrieve_extension_value(
|
|
47
|
+
PARAMETER_URL,
|
|
48
|
+
{
|
|
49
|
+
"name": parameter_name,
|
|
50
|
+
}
|
|
51
|
+
)['Parameter']['Value']
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print("Got an exception while trying to get ssm value from cache")
|
|
54
|
+
print(e)
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_secret_value_from_cache(secret_id: str) -> Optional[str]:
|
|
59
|
+
try:
|
|
60
|
+
return retrieve_extension_value(
|
|
61
|
+
SECRETS_URL,
|
|
62
|
+
{
|
|
63
|
+
"secretId": secret_id,
|
|
64
|
+
}
|
|
65
|
+
)['SecretString']
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print("Got an exception while trying to get secret value from cache")
|
|
68
|
+
print(e)
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def get_secretsmanager_client() -> 'SecretsManagerClient':
|
|
72
|
+
return boto3.client('secretsmanager')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_ssm_client() -> 'SSMClient':
|
|
76
|
+
return boto3.client('ssm')
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_secret_value(secret_id) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Collect the secret value
|
|
82
|
+
:param secret_id:
|
|
83
|
+
:return:
|
|
84
|
+
"""
|
|
85
|
+
secret_value_cached = get_secret_value_from_cache(secret_id)
|
|
86
|
+
if secret_value_cached is not None:
|
|
87
|
+
return secret_value_cached
|
|
88
|
+
|
|
89
|
+
# Get the boto3 response
|
|
90
|
+
get_secret_value_response = get_secretsmanager_client().get_secret_value(SecretId=secret_id)
|
|
91
|
+
|
|
92
|
+
return get_secret_value_response['SecretString']
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_ssm_value(parameter_name) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Collect the parameter from SSM
|
|
98
|
+
:param parameter_name:
|
|
99
|
+
:return:
|
|
100
|
+
"""
|
|
101
|
+
ssm_parameter_cached = get_ssm_value_from_cache(parameter_name)
|
|
102
|
+
if ssm_parameter_cached is not None:
|
|
103
|
+
return ssm_parameter_cached
|
|
104
|
+
|
|
105
|
+
# Get the boto3 response
|
|
106
|
+
get_ssm_parameter_response = get_ssm_client().get_parameter(Name=parameter_name)
|
|
107
|
+
|
|
108
|
+
return get_ssm_parameter_response['Parameter']['Value']
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_icav2_access_token() -> str:
|
|
112
|
+
"""
|
|
113
|
+
From the AWS Secrets Manager, retrieve the OrcaBus token.
|
|
114
|
+
:return:
|
|
115
|
+
"""
|
|
116
|
+
return get_secret_value(environ.get("ICAV2_ACCESS_TOKEN_SECRET_ID"))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Imports
|
|
4
|
+
from typing import Optional, Dict
|
|
5
|
+
|
|
6
|
+
from .globals import ICAV2_WES_SUBDOMAIN_NAME
|
|
7
|
+
from .models import WESRequest
|
|
8
|
+
from ..utils.requests_helpers import (
|
|
9
|
+
get_request, get_url, patch_request, get_request_response_results
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Wrappers
|
|
14
|
+
def get_icav2_wes_url(endpoint: str) -> str:
|
|
15
|
+
"""
|
|
16
|
+
Get the URL for the Metadata endpoint
|
|
17
|
+
:param endpoint:
|
|
18
|
+
:return:
|
|
19
|
+
"""
|
|
20
|
+
return get_url(
|
|
21
|
+
endpoint=endpoint,
|
|
22
|
+
subdomain=ICAV2_WES_SUBDOMAIN_NAME,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_icav2_wes_request(
|
|
27
|
+
endpoint: str,
|
|
28
|
+
params: Optional[Dict] = None,
|
|
29
|
+
):
|
|
30
|
+
return get_request(
|
|
31
|
+
url=get_icav2_wes_url(endpoint),
|
|
32
|
+
params=params
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_icav2_wes_request_response_results(
|
|
37
|
+
endpoint: str,
|
|
38
|
+
params: Optional[Dict] = None,
|
|
39
|
+
):
|
|
40
|
+
return get_request_response_results(
|
|
41
|
+
url=get_icav2_wes_url(endpoint),
|
|
42
|
+
params=params
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def icav2_wes_patch_request(
|
|
47
|
+
endpoint: str,
|
|
48
|
+
params: Optional[Dict] = None,
|
|
49
|
+
):
|
|
50
|
+
return patch_request(
|
|
51
|
+
url=get_icav2_wes_url(endpoint),
|
|
52
|
+
params=params
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def icav2_wes_post_request(
|
|
57
|
+
endpoint: str,
|
|
58
|
+
params: Dict
|
|
59
|
+
):
|
|
60
|
+
# Confirm param keys are valid
|
|
61
|
+
return patch_request(
|
|
62
|
+
url=get_icav2_wes_url(endpoint),
|
|
63
|
+
params=params
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# Set all
|
|
68
|
+
from create_helpers import create_icav2_wes_analysis
|
|
69
|
+
from query_helpers import get_icav2_wes_analysis_by_name
|
|
70
|
+
from update_helpers import update_icav2_wes_analysis_status
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
# Launch helpers
|
|
74
|
+
'create_icav2_wes_analysis',
|
|
75
|
+
# Query helpers
|
|
76
|
+
'get_icav2_wes_analysis_by_name',
|
|
77
|
+
# Update helpers
|
|
78
|
+
'update_icav2_wes_analysis_status'
|
|
79
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Create the job
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Unpack
|
|
8
|
+
|
|
9
|
+
from . import icav2_wes_post_request
|
|
10
|
+
from .globals import ANALYSES_ENDPOINT
|
|
11
|
+
from .models import WESRequest, WESResponse
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_icav2_wes_analysis(
|
|
15
|
+
**kwargs: Unpack[WESRequest]
|
|
16
|
+
) -> WESResponse:
|
|
17
|
+
"""
|
|
18
|
+
Launch an icav2 analysis in the ICAV2 WES API.
|
|
19
|
+
"""
|
|
20
|
+
# Confirm param keys are valid
|
|
21
|
+
for key in kwargs.keys():
|
|
22
|
+
if key not in WESRequest.__annotations__:
|
|
23
|
+
raise ValueError(f"Invalid parameter key: {key}. Valid keys are: {', '.join(WESRequest.__annotations__.keys())}")
|
|
24
|
+
|
|
25
|
+
# Confirm param values are valid
|
|
26
|
+
return icav2_wes_post_request(
|
|
27
|
+
ANALYSES_ENDPOINT,
|
|
28
|
+
params=dict(**kwargs)
|
|
29
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TypedDict, Dict, Any, Literal, NotRequired
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
AnalysisStorageSize = Literal['SMALL', 'MEDIUM', 'LARGE']
|
|
6
|
+
STATUS = Literal[
|
|
7
|
+
'SUBMITTED',
|
|
8
|
+
'PENDING',
|
|
9
|
+
'RUNNABLE',
|
|
10
|
+
'STARTING',
|
|
11
|
+
'RUNNING',
|
|
12
|
+
'SUCCEEDED',
|
|
13
|
+
'FAILED',
|
|
14
|
+
'ABORTED',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EngineParameters(TypedDict):
|
|
19
|
+
# Launch Configurations
|
|
20
|
+
pipelineId: str
|
|
21
|
+
projectId: str
|
|
22
|
+
analysisStorageSize: AnalysisStorageSize
|
|
23
|
+
# Locations
|
|
24
|
+
outputUri: str
|
|
25
|
+
logsUri: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class WESRequest(TypedDict):
|
|
29
|
+
"""WES request object."""
|
|
30
|
+
name: str
|
|
31
|
+
inputs: Dict[str, Any]
|
|
32
|
+
engineParameters: EngineParameters
|
|
33
|
+
tags: NotRequired[Dict[str, Any]]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class WESResponse(TypedDict):
|
|
37
|
+
"""WES response object."""
|
|
38
|
+
id: str
|
|
39
|
+
name: str
|
|
40
|
+
state: str
|
|
41
|
+
inputs: Dict[str, Any]
|
|
42
|
+
outputs: NotRequired[Dict[str, Any]]
|
|
43
|
+
engineParameters: EngineParameters
|
|
44
|
+
tags: NotRequired[Dict[str, Any]]
|
|
45
|
+
createdAt: str
|
|
46
|
+
updatedAt: str
|
|
47
|
+
projectId: str
|
|
48
|
+
pipelineId: str
|
|
49
|
+
status: str
|
|
50
|
+
errorMessage: str
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from . import get_icav2_wes_request_response_results
|
|
4
|
+
from .globals import ANALYSES_ENDPOINT
|
|
5
|
+
from .models import WESResponse
|
|
6
|
+
|
|
7
|
+
# Get the icav2 wes analysis by analysis name
|
|
8
|
+
def get_icav2_wes_analysis_by_name(analysis_name: str) -> WESResponse:
|
|
9
|
+
"""
|
|
10
|
+
Get the icav2 wes analysis by analysis name
|
|
11
|
+
"""
|
|
12
|
+
response_results = get_icav2_wes_request_response_results(
|
|
13
|
+
ANALYSES_ENDPOINT,
|
|
14
|
+
params={"name": analysis_name}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if not len(response_results) == 1:
|
|
18
|
+
raise ValueError(f"Expected 1 result, got {len(response_results)}")
|
|
19
|
+
|
|
20
|
+
return response_results[0]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from . import icav2_wes_patch_request
|
|
4
|
+
from .globals import ANALYSES_ENDPOINT
|
|
5
|
+
from .models import WESResponse
|
|
6
|
+
|
|
7
|
+
# Get the icav2 wes analysis by analysis name
|
|
8
|
+
def update_icav2_wes_analysis_status(
|
|
9
|
+
icav2_wes_orcabus_id: str,
|
|
10
|
+
status: str
|
|
11
|
+
) -> WESResponse:
|
|
12
|
+
"""
|
|
13
|
+
Get the icav2 wes analysis by analysis name
|
|
14
|
+
"""
|
|
15
|
+
response_results = icav2_wes_patch_request(
|
|
16
|
+
f"{ANALYSES_ENDPOINT}/{icav2_wes_orcabus_id}",
|
|
17
|
+
params={"status": status}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if not len(response_results) == 1:
|
|
21
|
+
raise ValueError(f"Expected 1 result, got {len(response_results)}")
|
|
22
|
+
|
|
23
|
+
return response_results[0]
|