diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f29b836..cbb2c5f 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -49,9 +49,9 @@ jobs: echo "is_semantic_tag=$IS_SEMANTIC_TAG" >> $GITHUB_OUTPUT - name: Upload image - uses: ishworkh/docker-image-artifact-upload@v1 + uses: ishworkh/container-image-artifact-upload@v2.0.0 with: - image: "sapi-python-client" + image: ${{ env.APP_IMAGE }} retention_days: "1" tests_aws: @@ -63,9 +63,9 @@ jobs: uses: actions/checkout@v3 - name: Download image - uses: ishworkh/docker-image-artifact-download@v1 + uses: ishworkh/container-image-artifact-download@v2.0.0 with: - image: "sapi-python-client" + image: ${{ env.APP_IMAGE }} - name: Run Tests run: | @@ -80,9 +80,9 @@ jobs: uses: actions/checkout@v3 - name: Download image - uses: ishworkh/docker-image-artifact-download@v1 + uses: ishworkh/container-image-artifact-download@v2.0.0 with: - image: "sapi-python-client" + image: ${{ env.APP_IMAGE }} - name: Run Tests run: | @@ -97,9 +97,9 @@ jobs: uses: actions/checkout@v3 - name: Download image - uses: ishworkh/docker-image-artifact-download@v1 + uses: ishworkh/container-image-artifact-download@v2.0.0 with: - image: "sapi-python-client" + image: ${{ env.APP_IMAGE }} - name: Run Tests run: | diff --git a/kbcstorage/branches.py b/kbcstorage/branches.py index 1e4ecd9..10c1520 100644 --- a/kbcstorage/branches.py +++ b/kbcstorage/branches.py @@ -41,3 +41,14 @@ def metadata(self, branch_id="default"): url = f"{self.base_url}branch/{branch_id}/metadata" return self._get(url) + + def branch_detail(self, branch_id="default"): + """ + Get branch details + """ + + if not isinstance(branch_id, str) or branch_id == "": + raise ValueError(f"Invalid branch_id '{branch_id}'") + + url = f"{self.base_url}dev-branches/{branch_id}" + return self._get(url) diff --git a/kbcstorage/configurations.py b/kbcstorage/configurations.py index 0e2e8da..23331f2 100644 --- a/kbcstorage/configurations.py +++ b/kbcstorage/configurations.py @@ -79,6 +79,22 @@ def list(self, component_id): url = '{}/{}/configs'.format(self.base_url, component_id) return self._get(url) + def list_config_workspaces(self, component_id, config_id): + """ + Lists workspaces for component configuration. + + Args: + component_id (str): The id of the component. + config_id (str): The id of the configuration. + + Raises: + requests.HTTPError: If the API request fails. + """ + if not isinstance(component_id, str) or component_id == '': + raise ValueError("Invalid component_id '{}'.".format(component_id)) + url = f'{self.base_url}/{component_id}/configs/{config_id}/workspaces' + return self._get(url) + def create(self, component_id, name, description='', configuration=None, state=None, change_description='', is_disabled=False, configuration_id=None): """ diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index f6f3135..4638d1b 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -9,9 +9,10 @@ from kbcstorage.base import Endpoint from kbcstorage.files import Files from kbcstorage.jobs import Jobs +from typing import List # the legacy Workspaces class below unfortunately defines its own method called list -def _make_body(mapping, source_key='source'): +def _make_body(mapping, source_key='source', preserve: bool = True): """ Given a dict mapping Keboola tables to aliases, construct the body of the HTTP request to load said tables. @@ -21,7 +22,7 @@ def _make_body(mapping, source_key='source'): be loaded (ie. 'in.c-bucker.table_name') and values contain the aliases to which they will be loaded (ie. 'table_name'). """ - body = {} + body = {'preserve': str(preserve).lower()} template = 'input[{0}][{1}]' for i, (k, v) in enumerate(mapping.items()): body[template.format(i, source_key)] = k @@ -72,7 +73,7 @@ def detail(self, workspace_id): url = '{}/{}'.format(self.base_url, workspace_id) return self._get(url) - def create(self, backend=None, timeout=None): + def create(self, backend=None, timeout=None, login_type=None, public_key=None, read_all_objects=False): """ Create a new Workspace and return the credentials. @@ -87,7 +88,10 @@ def create(self, backend=None, timeout=None): """ body = { 'backend': backend, - 'statementTimeoutSeconds': timeout + 'statementTimeoutSeconds': timeout, + 'loginType': login_type, + 'publicKey': public_key, + 'readOnlyStorageAccess': str(read_all_objects).lower() # convert bool to lowercase true or false } return self._post(self.base_url, data=body) @@ -122,17 +126,28 @@ def reset_password(self, workspace_id): url = '{}/{}/password'.format(self.base_url, workspace_id) return self._post(url) - def load_tables(self, workspace_id, table_mapping, preserve=None): + def set_public_key(self, workspace_id, public_key): + """ + Set the public key for the workspace. + """ + data = { + 'publicKey': public_key + } + url = '{}/{}/public-key'.format(self.base_url, workspace_id) + return self._post(url, json=data) + + def load_tables(self, workspace_id: int | str, table_mapping: dict | List[dict], preserve=True, load_type='load'): """ Load tabes from storage into a workspace. Args: workspace_id (int or str): The id of the workspace to which to load the tables. - table_mapping (:obj:`dict`): Source table names mapped to - destination table names. + table_mapping (:obj:`dict` or :obj:`list`): Source table names mapped to + destination table names. or a list of dicts with detailed tables specification. preserve (bool): If False, drop tables, else keep tables in workspace. + load_type (str): Type of load, either 'load' or 'load-clone'. Defaults to 'load'. Raises: requests.HTTPError: If the API request fails. @@ -140,11 +155,21 @@ def load_tables(self, workspace_id, table_mapping, preserve=None): Todo: * Column data types. """ - body = _make_body(table_mapping) - body['preserve'] = preserve - url = '{}/{}/load'.format(self.base_url, workspace_id) + load_type = load_type.lower() + if load_type not in ['load', 'load-clone']: + raise ValueError("Invalid load_type: {}, supports only load and load-clone".format(load_type)) + + url = "/".join([self.base_url, str(workspace_id), load_type]) + + req = None + if isinstance(table_mapping, dict): + body = _make_body(table_mapping, preserve=preserve) + req = self._post(url, data=body) + elif isinstance(table_mapping, list): + body = {'input': table_mapping, 'preserve': str(preserve).lower()} + req = self._post(url, json=body) - return self._post(url, data=body) + return req def load_files(self, workspace_id, file_mapping): """