dnastack-client-library 3.1.114a0__py3-none-any.whl → 3.1.120a0__py3-none-any.whl

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.
@@ -1,4 +1,5 @@
1
1
  from dnastack.cli.commands.publisher.collections import collections_command_group
2
+ from dnastack.cli.commands.publisher.datasources import datasources_command_group
2
3
  from dnastack.cli.core.group import formatted_group
3
4
 
4
5
 
@@ -8,3 +9,4 @@ def publisher_command_group():
8
9
 
9
10
 
10
11
  publisher_command_group.add_command(collections_command_group)
12
+ publisher_command_group.add_command(datasources_command_group)
@@ -0,0 +1,9 @@
1
+ from dnastack.cli.core.group import formatted_group
2
+ from dnastack.cli.commands.publisher.datasources.commands import init_datasources_commands
3
+
4
+ @formatted_group("datasources")
5
+ def datasources_command_group():
6
+ """ Interact with data sources """
7
+
8
+ # Initialize all commands
9
+ init_datasources_commands(datasources_command_group)
@@ -0,0 +1,35 @@
1
+ from typing import Optional
2
+
3
+ from click import Group
4
+
5
+ from dnastack.cli.commands.publisher.datasources.utils import _get_datasource_client, _filter_datasource_fields
6
+ from dnastack.cli.core.command import formatted_command
7
+ from dnastack.cli.core.command_spec import RESOURCE_OUTPUT_ARG, ArgumentSpec
8
+ from dnastack.cli.helpers.iterator_printer import show_iterator
9
+ from dnastack.common.tracing import Span
10
+
11
+ def init_datasources_commands(group: Group):
12
+ @formatted_command(
13
+ group=group,
14
+ name='list',
15
+ specs=[
16
+ RESOURCE_OUTPUT_ARG,
17
+ ArgumentSpec(
18
+ name='type',
19
+ arg_names=['--type'],
20
+ help='Filter datasources by type (e.g., "AWS")',
21
+ required=False
22
+ ),
23
+ ]
24
+ )
25
+ def list_datasources(output: Optional[str] = None, type: Optional[str] = None):
26
+ """ List all data sources """
27
+ with Span("list_datasources") as span:
28
+ client = _get_datasource_client()
29
+ response = client.list_datasources(trace=span, type=type)
30
+
31
+ show_iterator(output,
32
+ [
33
+ _filter_datasource_fields(datasource.dict())
34
+ for datasource in response.connections
35
+ ])
@@ -0,0 +1,18 @@
1
+ from typing import Dict, Any, Optional
2
+ from imagination import container
3
+ from dnastack.cli.helpers.client_factory import ConfigurationBasedClientFactory
4
+ from dnastack.client.datasources.client import DataSourceServiceClient
5
+
6
+
7
+ def _get_datasource_client() -> DataSourceServiceClient:
8
+ """Get the data source service client."""
9
+ factory: ConfigurationBasedClientFactory = container.get(ConfigurationBasedClientFactory)
10
+ return factory.get(DataSourceServiceClient)
11
+
12
+ def _filter_datasource_fields(datasource: Dict[str, Any]) -> Dict[str, Any]:
13
+ """Filter and transform datasource fields for display."""
14
+ return {
15
+ 'id': datasource.get('id'),
16
+ 'name': datasource.get('name'),
17
+ 'type': datasource.get('type'),
18
+ }
@@ -3,6 +3,7 @@ from typing import TypeVar
3
3
  from dnastack.client.base_client import BaseServiceClient
4
4
  from dnastack.client.collections.client import CollectionServiceClient
5
5
  from dnastack.client.data_connect import DataConnectClient
6
+ from dnastack.client.datasources.client import DataSourceServiceClient
6
7
  from dnastack.client.drs import DrsClient
7
8
  from dnastack.client.service_registry.client import ServiceRegistry
8
9
  from dnastack.client.workbench.ewes.client import EWesClient
@@ -15,12 +16,12 @@ from dnastack.client.workbench.workflow.client import WorkflowClient
15
16
  ALL_SERVICE_CLIENT_CLASSES = (
16
17
  CollectionServiceClient, DataConnectClient, DrsClient, ServiceRegistry, EWesClient, StorageClient, SamplesClient,
17
18
  WorkflowClient,
18
- WorkbenchUserClient)
19
+ WorkbenchUserClient, DataSourceServiceClient)
19
20
 
20
21
  # All client classes for data access
21
22
  DATA_SERVICE_CLIENT_CLASSES = (
22
23
  CollectionServiceClient, DataConnectClient, DrsClient, EWesClient, StorageClient, WorkflowClient,
23
- WorkbenchUserClient)
24
+ WorkbenchUserClient, DataSourceServiceClient)
24
25
 
25
26
  # Type variable for the service client
26
27
  SERVICE_CLIENT_CLASS = TypeVar('SERVICE_CLIENT_CLASS',
@@ -33,4 +34,5 @@ SERVICE_CLIENT_CLASS = TypeVar('SERVICE_CLIENT_CLASS',
33
34
  DataConnectClient,
34
35
  DrsClient,
35
36
  SamplesClient,
36
- ServiceRegistry)
37
+ ServiceRegistry,
38
+ DataSourceServiceClient)
@@ -0,0 +1,3 @@
1
+ from dnastack.client.datasources.client import DataSource, DataSourcesResponse, DataSourceServiceClient
2
+
3
+ __all__ = ['DataSourceServiceClient', 'DataSource', 'DataSourcesResponse']
@@ -0,0 +1,124 @@
1
+ from pprint import pformat
2
+ from typing import List, Optional
3
+ from pydantic import BaseModel, ValidationError
4
+ from dnastack.client.base_client import BaseServiceClient
5
+ from dnastack.client.base_exceptions import (
6
+ UnauthenticatedApiAccessError, UnauthorizedApiAccessError
7
+ )
8
+ from dnastack.client.collections.client import STANDARD_COLLECTION_SERVICE_TYPE_V1_0
9
+ from dnastack.client.collections.model import PageableApiError
10
+ from dnastack.client.datasources.model import DataSource, DataSourceListOptions
11
+ from dnastack.client.result_iterator import ResultLoader, InactiveLoaderError, ResultIterator
12
+ from dnastack.http.session import HttpSession, HttpError
13
+ from dnastack.common.tracing import Span
14
+
15
+
16
+ class DataSourcesResponse(BaseModel):
17
+ connections: List[DataSource]
18
+
19
+ def items(self) -> List[DataSource]:
20
+ return self.connections
21
+
22
+ class DataSourceListResultLoader(ResultLoader):
23
+ """
24
+ Result loader for handling data source fetching without pagination.
25
+ """
26
+
27
+ def __init__(self, service_url: str, http_session: HttpSession, trace: Span,
28
+ list_options: Optional[dict] = None, max_results: Optional[int] = None):
29
+ self.__service_url = service_url
30
+ self.__http_session = http_session
31
+ self.__list_options = list_options or {}
32
+ self.__max_results = max_results
33
+ self.__loaded_results = 0
34
+ self.__active = True # Determines when we are done fetching results
35
+ self.__trace = trace
36
+
37
+ def has_more(self) -> bool:
38
+ """Checks if there are more results to fetch."""
39
+ return self.__active
40
+
41
+ def extract_api_response(self, response_body: dict) -> DataSourcesResponse:
42
+ """
43
+ Converts the API response body into a DataSourcesResponse object.
44
+ """
45
+ return DataSourcesResponse(**response_body)
46
+
47
+ def load(self) -> List[DataSource]:
48
+ """Fetches the data from the API."""
49
+ if not self.__active:
50
+ raise InactiveLoaderError(self.__service_url)
51
+
52
+ with self.__http_session as session:
53
+ try:
54
+ # Perform the GET request
55
+ response = session.get(
56
+ self.__service_url,
57
+ params=self.__list_options,
58
+ trace_context=self.__trace
59
+ )
60
+ except HttpError as e:
61
+ error_feedback = f"Failed to load data from {self.__service_url}: {e.response.text}"
62
+ if e.response.status_code == 401:
63
+ raise UnauthenticatedApiAccessError(error_feedback)
64
+ elif e.response.status_code == 403:
65
+ raise UnauthorizedApiAccessError(error_feedback)
66
+ else:
67
+ raise PageableApiError(error_feedback, e.response.status_code, e.response.text)
68
+
69
+ # Parse the API response
70
+ response_body = response.json() if response.text else {}
71
+ try:
72
+ api_response = self.extract_api_response(response_body)
73
+ except ValidationError:
74
+ raise PageableApiError(
75
+ f"Invalid response body: {response_body}",
76
+ response.status_code,
77
+ response.text,
78
+ )
79
+
80
+ # Retrieve the data source items
81
+ items = api_response.items()
82
+
83
+ # Deactivate the loader after the first fetch since there's no pagination
84
+ self.__active = False
85
+
86
+ # Handle max_results constraint
87
+ if self.__max_results and len(items) > self.__max_results:
88
+ return items[:self.__max_results]
89
+
90
+ return items
91
+
92
+ class DataSourceServiceClient(BaseServiceClient):
93
+ """
94
+ Client to interact with the Data Sources API.
95
+ """
96
+
97
+ @staticmethod
98
+ def get_adapter_type() -> str:
99
+ return 'collections'
100
+
101
+ @classmethod
102
+ def get_supported_service_types(cls) -> List[str]:
103
+ """
104
+ Returns supported service types.
105
+ """
106
+ return [ STANDARD_COLLECTION_SERVICE_TYPE_V1_0,]
107
+
108
+ def list_datasources(self, type: Optional[str] = None, trace: Optional[Span] = None,
109
+ max_results: Optional[int] = None) -> DataSourcesResponse:
110
+ # Set up query parameters
111
+ list_options = {}
112
+ if type:
113
+ list_options["type"] = type
114
+
115
+ trace = trace or Span(origin=self)
116
+ loader = DataSourceListResultLoader(
117
+ service_url=f"{self.url}/connections/data-sources",
118
+ http_session=self.create_http_session(),
119
+ trace=trace,
120
+ list_options=list_options,
121
+ max_results=max_results
122
+ )
123
+ connections = list(ResultIterator(loader))
124
+ return DataSourcesResponse(connections=connections)
@@ -0,0 +1,11 @@
1
+ from typing import Optional
2
+
3
+ from pydantic.main import BaseModel
4
+
5
+ class DataSourceListOptions(BaseModel):
6
+ pass
7
+
8
+ class DataSource(BaseModel):
9
+ id: Optional[str]
10
+ name: Optional[str]
11
+ type: Optional[str]
dnastack/constants.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import os
2
2
 
3
- __version__ = "v3.1.114a"
3
+ __version__ = "v3.1.120a"
4
4
 
5
5
  LOCAL_STORAGE_DIRECTORY = os.path.join(os.path.expanduser("~"), '.dnastack')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dnastack-client-library
3
- Version: 3.1.114a0
3
+ Version: 3.1.120a0
4
4
  Summary: "DNAstack's GA4GH library and CLI"
5
5
  Home-page: https://www.dnastack.com
6
6
  Author: DNAstack
@@ -1,6 +1,6 @@
1
1
  dnastack/__init__.py,sha256=Bpfm77MeoZNmZHSgDK9wlgVPkcdE73ur6_7Zihorb4o,263
2
2
  dnastack/__main__.py,sha256=3rydT8oj5G1IN0asiqv9oaQw2xmyIlVge8c8wUjo0HA,3532
3
- dnastack/constants.py,sha256=cUGMUqc8saQdOufZDxdRVDtT5y1Rt1VKT4bh1Hz_6HY,115
3
+ dnastack/constants.py,sha256=emInQGO_r53MWwHbI63wDj3e9qlVQaEQ5oP5zP86Qx0,115
4
4
  dnastack/feature_flags.py,sha256=RK_V_Ovncoe6NeTheAA_frP-kYkZC1fDlTbbup2KYG4,1419
5
5
  dnastack/json_path.py,sha256=TyghhDf7nGQmnsUWBhenU_fKsE_Ez-HLVER6HgH5-hU,2700
6
6
  dnastack/omics_cli.py,sha256=ZppKZTHv_XjUUZyRIzSkx0Ug5ODAYrCOTsU0ezCOVrA,3694
@@ -49,12 +49,15 @@ dnastack/cli/commands/dataconnect/utils.py,sha256=7psRouHUsg2QEemZAhzHVsjy1rza63
49
49
  dnastack/cli/commands/drs/__init__.py,sha256=XGPfCsdOyZyc67ptYmM903US741hDCKaTowVtysC6H8,325
50
50
  dnastack/cli/commands/drs/commands.py,sha256=9n-f24ODZNCpxgIirdzsIEnZqVRw2jFkvXiCsW4ZnOI,5405
51
51
  dnastack/cli/commands/drs/utils.py,sha256=tGogaIlXKnk03GqitzeDfo893JlP7Nc_X28sH-yQUrI,427
52
- dnastack/cli/commands/publisher/__init__.py,sha256=1dILe4uH1omguGjPQYbxMysdxsA2fUy7wvk5hzq41kA,298
52
+ dnastack/cli/commands/publisher/__init__.py,sha256=G8WNdx1UwanoA4X349ghv8Zyv5YizB8ryeJmwu9px8o,443
53
53
  dnastack/cli/commands/publisher/collections/__init__.py,sha256=KmclN_KY3ctVhtv-i8rxXpWTshPCj1tY6yhud4vrXYQ,636
54
54
  dnastack/cli/commands/publisher/collections/commands.py,sha256=A82NphvnD-9JuN2dVU_07EbAvB1NE7Em07IU7eDhagc,9454
55
55
  dnastack/cli/commands/publisher/collections/items.py,sha256=gmSCaXXqxk4lElZnqkU6KZCLa5QSDgTd5Zz0LWSBMDQ,6101
56
56
  dnastack/cli/commands/publisher/collections/tables.py,sha256=N348MU_p-OKweJwKYtNbpvCD3ZmGGbS-quz7gnsAc1c,2130
57
57
  dnastack/cli/commands/publisher/collections/utils.py,sha256=GNgGv29z7eumQqbw1IFFasJeDyxH2KQGQl0q6UPooVU,6244
58
+ dnastack/cli/commands/publisher/datasources/__init__.py,sha256=90suC61lfFF1WzIU9f8lO0_DiLWIQF2Dy46_Yad3CD8,327
59
+ dnastack/cli/commands/publisher/datasources/commands.py,sha256=TdR55CxqlpN62GiHeWIfPfRsorfrzDj7GwZlmBqseA0,1298
60
+ dnastack/cli/commands/publisher/datasources/utils.py,sha256=DGVZY6e0Faa0sNRXxqu50z7kGACX4YYkF-cgtMLaUQc,745
58
61
  dnastack/cli/commands/workbench/__init__.py,sha256=H4CGbc31-21mRZqJMtzi2Cg4e_D9a9ibqFjwXQTcXNY,1092
59
62
  dnastack/cli/commands/workbench/utils.py,sha256=V2I192k4XAg2h-DrsdatqIZfguflh6WR7q5pSBojUX0,4354
60
63
  dnastack/cli/commands/workbench/engines/__init__.py,sha256=qB1rfGXNJ6_N6F5UYXos4hHDwhm8PtlbrCCt_uNhIIw,653
@@ -103,7 +106,7 @@ dnastack/cli/helpers/printer.py,sha256=IdGoEnBWtzgPAE2KLoZEfwshn0tkrTu-XXtN-KXj8
103
106
  dnastack/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
107
  dnastack/client/base_client.py,sha256=ZhaWHHTQcuPun6RdrtCdShGRYoY18hEKAIHmr8Z728M,3730
105
108
  dnastack/client/base_exceptions.py,sha256=QoYa-LzT5C0o7eq6Ek4R_-clBL0nZ5v7qdFBcCoUiw4,2888
106
- dnastack/client/constants.py,sha256=vV4ea5WrYJeCIfcC6YcNfh6F16SauFjBG2dhkupwAek,1672
109
+ dnastack/client/constants.py,sha256=6nB1QKz-GEx8BSnPLFafKGG1WDF3l0P0_HlC01FSvwc,1849
107
110
  dnastack/client/data_connect.py,sha256=6qDx6-dl-fmakEMYK_jfTgUcYe1yl8eTCMPI_8tRUwg,26211
108
111
  dnastack/client/drs.py,sha256=7iNu58YAtQHKFFFNllFyXATfasT_ndkgSkBKKgDbiKc,21225
109
112
  dnastack/client/factory.py,sha256=PQGYUGhKeqyJtEtc-bZM5OlEup9K7lB-qwmNiII_4HU,6288
@@ -112,6 +115,9 @@ dnastack/client/result_iterator.py,sha256=00zEs7YbPJyt8d-6j7eHnmSVurP8hIOarj4Fws
112
115
  dnastack/client/collections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
116
  dnastack/client/collections/client.py,sha256=ea7N7fDP5jdyPT3_gwd3WVm_wsGZ4ApTm6qdUr3X70k,15376
114
117
  dnastack/client/collections/model.py,sha256=JO-eC_jc7hY9PBvST3x1vkmJE0LPDAbcUq5VBKcGyLM,3492
118
+ dnastack/client/datasources/__init__.py,sha256=HxDIHuQX8KMWr3o70ucL3x79pXKaIHbBq7JqmyoRGxM,179
119
+ dnastack/client/datasources/client.py,sha256=AfYxu18QRLh3tNe6uwS_Z00VwZCZNw7fUYMsEDDMe40,4734
120
+ dnastack/client/datasources/model.py,sha256=dV9Sf05ivIq0ubwIIYK3kSv1xJ_TtjxvVp_ddI9aHEk,214
115
121
  dnastack/client/service_registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
122
  dnastack/client/service_registry/client.py,sha256=r7D8CnPJLbNkc03g2PYHt880Ba1oPW2d8B0ShP0p4Eo,1131
117
123
  dnastack/client/service_registry/factory.py,sha256=l_H4ipFzv9j6VkWoPtyHOHJvCCmS6ceVpazjD5qfXM0,10741
@@ -177,9 +183,9 @@ dnastack/http/authenticators/oauth2_adapter/client_credential.py,sha256=cCVZa4B1
177
183
  dnastack/http/authenticators/oauth2_adapter/device_code_flow.py,sha256=_OMHPf7qekPn_oSCBb41iYg9100sQMVzfJiKZHdh26w,6529
178
184
  dnastack/http/authenticators/oauth2_adapter/factory.py,sha256=r8K6swt5zhraP74KhTL2K4sQ71HWAMLM0oHg8qQT4BA,965
179
185
  dnastack/http/authenticators/oauth2_adapter/models.py,sha256=U11r8DZsWvjIRNCJE1mmQMuprZw3fpFwFBg7vmI5w48,660
180
- dnastack_client_library-3.1.114a0.dist-info/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
181
- dnastack_client_library-3.1.114a0.dist-info/METADATA,sha256=c0GocQcklX10DyFUQqapEvE9FRjeObALAsTgBLA0YAU,968
182
- dnastack_client_library-3.1.114a0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
183
- dnastack_client_library-3.1.114a0.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
184
- dnastack_client_library-3.1.114a0.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
185
- dnastack_client_library-3.1.114a0.dist-info/RECORD,,
186
+ dnastack_client_library-3.1.120a0.dist-info/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
187
+ dnastack_client_library-3.1.120a0.dist-info/METADATA,sha256=6fbWkDqg_o_lE-mKC7oDKWMkqeqKlHl3s8dDDdsKBeU,968
188
+ dnastack_client_library-3.1.120a0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
189
+ dnastack_client_library-3.1.120a0.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
190
+ dnastack_client_library-3.1.120a0.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
191
+ dnastack_client_library-3.1.120a0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5