From c36d5d53e2d0c8f8aba927d7cb40e1b834419613 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Fri, 12 Mar 2021 19:59:04 +0100 Subject: [PATCH 01/19] add workspace tests --- Dockerfile | 2 +- kbcstorage/workspaces.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index aa00aa8..cf144fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,6 @@ FROM python:3.6 WORKDIR /code COPY . /code/ -RUN pip3 install --no-cache-dir flake8 responses +RUN pip3 install --no-cache-dir flake8 responses snowflake-connector-python RUN python setup.py install ENTRYPOINT ["python"] diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index 97ac7fd..6e4950b 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -76,7 +76,7 @@ def create(self, backend=None, timeout=None): Args: backend (:obj:`str`): The type of engine for the workspace. - 'redshift' or 'snowflake'. Default redshift. + 'redshift', 'snowflake' or 'synapse'. Defaults to the project's default backend. timeout (int): The timeout, in seconds, for SQL statements. Only supported by snowflake backends. From 645dd3eec0764577eeb58b05d6a82b31c7d0ac32 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 10:09:26 +0100 Subject: [PATCH 02/19] add workspaces tests --- tests/functional/test_workspaces.py | 118 ++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/functional/test_workspaces.py diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py new file mode 100644 index 0000000..28b7e27 --- /dev/null +++ b/tests/functional/test_workspaces.py @@ -0,0 +1,118 @@ +import csv +import os +import snowflake.connector +import tempfile +import time +import unittest +import warnings + +from azure.storage.blob import BlobServiceClient +from requests import exceptions +from kbcstorage.workspaces import Workspaces +from kbcstorage.buckets import Buckets +from kbcstorage.tables import Tables +from kbcstorage.jobs import Jobs + + +class TestWorkspaces(unittest.TestCase): + def setUp(self): + self.workspaces = Workspaces(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) + self.buckets = Buckets(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) + self.tables = Tables(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) + try: + self.buckets.delete('in.c-py-test-buckets', force=True) + except exceptions.HTTPError as e: + if e.response.status_code != 404: + raise + # https://github.com/boto/boto3/issues/454 + warnings.simplefilter("ignore", ResourceWarning) + + def tearDown(self): + try: + if self.workspace_id: + self.workspaces.delete(self.workspace_id) + except exceptions.HTTPError as e: + if e.response.status_code != 404: + raise + try: + self.buckets.delete('in.c-py-test-tables', force=True) + except exceptions.HTTPError as e: + if e.response.status_code != 404: + raise + + def test_create_workspace(self): + workspace = self.workspaces.create() + self.workspace_id = workspace['id'] + with self.subTest(): + self.assertTrue('id' in workspace) + with self.subTest(): + self.assertTrue('type' in workspace) + self.assertTrue(workspace['type'] in ['table', 'file']) + with self.subTest(): + self.assertTrue('name' in workspace) + with self.subTest(): + self.assertTrue('component' in workspace) + with self.subTest(): + self.assertTrue('configurationId' in workspace) + with self.subTest(): + self.assertTrue('created' in workspace) + with self.subTest(): + self.assertTrue('connection' in workspace) + with self.subTest(): + self.assertTrue('backend' in workspace['connection']) + with self.subTest(): + self.assertTrue('creatorToken' in workspace) + + def test_load_tables_to_workspace(self): + bucket_id = self.buckets.create('py-test-tables') + table1_id = self.__create_table(bucket_id, 'test-table-1', {'col1': 'ping', 'col2': 'pong'}) + table2_id = self.__create_table(bucket_id, 'test-table-2', {'col1': 'king', 'col2': 'kong'}) + workspace = self.workspaces.create() + self.workspace_id = workspace['id'] + job = self.workspaces.load_tables( + workspace['id'], + {table1_id: 'destination_1', table2_id: 'destination_2'} + ) + Jobs.block_until_completed(job['id']) + conn = self.__get_snowflake_conenction(workspace['connection']) + cursor = conn.cursor() + cursor.execute('SELECT * FROM "destination_1"') + self.assertEquals(('ping', 'pong'), cursor.fetchone()) + cursor.execute('SELECT * FROM "destination_2"') + self.assertEquals(('king', 'kong'), cursor.fetchone()) + + # test load files into an abs workspace + def test_load_files_from_workspace(self): + workspace = self.workspaces.create('abs') + self.workspace_id = workspace['id'] + job = self.workspaces.load_files( + workspace['id'], + {{'tags': ['sapi-client-pythen-tests']}} + ) + Jobs.block_until_completed(job['id']) + blob_service_client = BlobServiceClient.from_connection_string(workspace['connection']['connectionString']) + container_client = blob_service_client.get_container_client( + container=workspace['connection']['container'] + ) + blob_list = container_client.list_blobs() + for blob in blob_list: + print(blob) + + def __create_table(self, bucket_id, table_name, row): + file, path = tempfile.mkstemp(prefix='sapi-test') + with open(path, 'w') as csv_file: + writer = csv.DictWriter(csv_file, fieldnames=['col1', 'col2'], + lineterminator='\n', delimiter=',', quotechar='"') + writer.writeheader() + writer.writerow(row) + return self.tables.create(name=table_name, file_path=path, bucket_id=bucket_id) + + def __get_snowflake_conenction(self, connection_properties): + return snowflake.connector.connect( + user=connection_properties['user'], + password=connection_properties['password'], + account=connection_properties['host'].split('.snowflakecomputing')[0], + warehouse=connection_properties['warehouse'], + database=connection_properties['database'], + schema=connection_properties['schema'] + ) From 7098904762eea3215fccd1d653ab4da3c2bd2059 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 17:51:55 +0100 Subject: [PATCH 03/19] load files to workspace --- kbcstorage/workspaces.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index 6e4950b..5312d0e 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -7,6 +7,7 @@ http://docs.keboola.apiary.io/#reference/workspaces/ """ from kbcstorage.base import Endpoint +from kbcstorage.files import Files def _make_body(mapping): @@ -143,3 +144,26 @@ def load_tables(self, workspace_id, table_mapping, preserve=None): url = '{}/{}/load'.format(self.base_url, workspace_id) return self._post(url, data=body) + + # only supports abs workspace + # the workspace must be the object returned by the create method otherwise it won't have the connectionString + def load_files(self, workspace, file_mapping, preserve=None): + if (workspace['type'] != 'file' and workspace['connection']['backend'] != 'abs'): + raise Exception('Loading files to workspace is only available for ABS workspaces') + files = Files(self.root_url, self.token) + file_list = files.list(tags=file_mapping['tags']) + inputs = [] + for file in file_list: + inputs.append({ + 'dataFileId': file['id'], + 'destination': file_mapping['destination'] + }) + body = {} + i = 0 + for input in inputs: + body['input[%s][dataFileId]' % (i)] = input['dataFileId'] + body['input[%s][destination]' % (i)] = input['destination'] + i += 1 + body['preserve'] = preserve + url = '{}/{}/load'.format(self.base_url, workspace['id']) + return self._post(url, data=body) From dad7782be25a41780710b0cef2e25462ec865e7d Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 17:52:11 +0100 Subject: [PATCH 04/19] test load files to workspace --- tests/functional/test_workspaces.py | 45 ++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 28b7e27..3ab5358 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -8,17 +8,27 @@ from azure.storage.blob import BlobServiceClient from requests import exceptions -from kbcstorage.workspaces import Workspaces from kbcstorage.buckets import Buckets -from kbcstorage.tables import Tables from kbcstorage.jobs import Jobs +from kbcstorage.files import Files +from kbcstorage.tables import Tables +from kbcstorage.workspaces import Workspaces class TestWorkspaces(unittest.TestCase): def setUp(self): self.workspaces = Workspaces(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) self.buckets = Buckets(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) + self.jobs = Jobs(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) self.tables = Tables(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) + self.files = Files(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) + try: + file_list = self.files.list(tags=['sapi-client-pythen-tests']) + for file in file_list: + self.files.delete(file['id']) + except exceptions.HTTPError as e: + if e.response.status_code != 404: + raise try: self.buckets.delete('in.c-py-test-buckets', force=True) except exceptions.HTTPError as e: @@ -64,7 +74,7 @@ def test_create_workspace(self): self.assertTrue('creatorToken' in workspace) def test_load_tables_to_workspace(self): - bucket_id = self.buckets.create('py-test-tables') + bucket_id = self.buckets.create('py-test-tables')['id'] table1_id = self.__create_table(bucket_id, 'test-table-1', {'col1': 'ping', 'col2': 'pong'}) table2_id = self.__create_table(bucket_id, 'test-table-2', {'col1': 'king', 'col2': 'kong'}) workspace = self.workspaces.create() @@ -73,30 +83,37 @@ def test_load_tables_to_workspace(self): workspace['id'], {table1_id: 'destination_1', table2_id: 'destination_2'} ) - Jobs.block_until_completed(job['id']) + self.jobs.block_until_completed(job['id']) conn = self.__get_snowflake_conenction(workspace['connection']) cursor = conn.cursor() cursor.execute('SELECT * FROM "destination_1"') - self.assertEquals(('ping', 'pong'), cursor.fetchone()) + self.assertEqual(('ping', 'pong'), cursor.fetchone()) cursor.execute('SELECT * FROM "destination_2"') - self.assertEquals(('king', 'kong'), cursor.fetchone()) + self.assertEqual(('king', 'kong'), cursor.fetchone()) # test load files into an abs workspace def test_load_files_from_workspace(self): + # put a test file to storage + file, path = tempfile.mkstemp(prefix='sapi-test') + os.write(file, bytes('fooBar', 'utf-8')) + file_id = self.files.upload_file(path, tags=['sapi-client-pythen-tests', 'file1']) + + # create a workspace and load the file to it workspace = self.workspaces.create('abs') self.workspace_id = workspace['id'] job = self.workspaces.load_files( - workspace['id'], - {{'tags': ['sapi-client-pythen-tests']}} + workspace, + {'tags': ['sapi-client-pythen-tests'], 'destination': 'data/in/files'} ) - Jobs.block_until_completed(job['id']) + self.jobs.block_until_completed(job['id']) + + # assert that the file was loaded to the workspace blob_service_client = BlobServiceClient.from_connection_string(workspace['connection']['connectionString']) - container_client = blob_service_client.get_container_client( - container=workspace['connection']['container'] + blob_client = blob_service_client.get_blob_client( + container=workspace['connection']['container'], + blob='data/in/files/%s' % str(file_id) ) - blob_list = container_client.list_blobs() - for blob in blob_list: - print(blob) + self.assertEqual('fooBar', blob_client.download_blob().readall().decode('utf-8')) def __create_table(self, bucket_id, table_name, row): file, path = tempfile.mkstemp(prefix='sapi-test') From aa2c4fb8c8655a511bb532a10840f24f337582e7 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 18:05:25 +0100 Subject: [PATCH 05/19] skip file workspace test on aws --- .travis.yml | 2 +- tests/functional/test_workspaces.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2271f05..b53122e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_script: - docker-compose build sapi-python-client script: - docker-compose run --rm --entrypoint=flake8 sapi-python-client -- docker-compose run --rm -e KBC_TEST_TOKEN -e KBC_TEST_API_URL sapi-python-client -m unittest discover +- docker-compose run --rm -e KBC_TEST_TOKEN -e KBC_TEST_API_URL -e SKIP_ABS_TEST=1 sapi-python-client -m unittest discover - docker-compose run --rm -e KBC_TEST_TOKEN=$KBC_AZ_TEST_TOKEN -e KBC_TEST_API_URL=$KBC_AZ_TEST_API_URL sapi-python-client -m unittest discover after_success: - docker images diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 3ab5358..ce1d6a3 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -93,6 +93,8 @@ def test_load_tables_to_workspace(self): # test load files into an abs workspace def test_load_files_from_workspace(self): + if (os.environ['SKIP_ABS_TESTS']): + self.skipTest('Skipping ABS test because env var SKIP_ABS_TESTS was set') # put a test file to storage file, path = tempfile.mkstemp(prefix='sapi-test') os.write(file, bytes('fooBar', 'utf-8')) From bf0029e3771c52a395c36a94b55527e683fa43d4 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 18:38:40 +0100 Subject: [PATCH 06/19] fix check env var --- tests/functional/test_workspaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index ce1d6a3..3009a84 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -93,7 +93,7 @@ def test_load_tables_to_workspace(self): # test load files into an abs workspace def test_load_files_from_workspace(self): - if (os.environ['SKIP_ABS_TESTS']): + if (os.getenv('SKIP_ABS_TEST')): self.skipTest('Skipping ABS test because env var SKIP_ABS_TESTS was set') # put a test file to storage file, path = tempfile.mkstemp(prefix='sapi-test') From c562c5d9493b378aeb7ddbfc66c2545eeb990455 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 19:06:43 +0100 Subject: [PATCH 07/19] add test for invalid workspace load files --- tests/functional/test_workspaces.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 3009a84..02780a1 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -117,6 +117,18 @@ def test_load_files_from_workspace(self): ) self.assertEqual('fooBar', blob_client.download_blob().readall().decode('utf-8')) + def test_load_files_invalid_workspace(self): + workspace = self.workspaces.create() + self.workspace_id = workspace['id'] + try: + job = self.workspaces.load_files( + workspace, + {'tags': ['sapi-client-pythen-tests'], 'destination': 'data/in/files'} + ) + self.assertFail() + except Exception as exception: + self.assertEqual('Loading files to workspace is only available for ABS workspaces', str(exception)) + def __create_table(self, bucket_id, table_name, row): file, path = tempfile.mkstemp(prefix='sapi-test') with open(path, 'w') as csv_file: From 84b1abfae969c5acb69f7455261cc93396b8f9d4 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 20:14:52 +0100 Subject: [PATCH 08/19] check id is set before deletion attempt --- tests/functional/test_workspaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 02780a1..e27df27 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -39,7 +39,7 @@ def setUp(self): def tearDown(self): try: - if self.workspace_id: + if hasattr(self, 'workspace_id'): self.workspaces.delete(self.workspace_id) except exceptions.HTTPError as e: if e.response.status_code != 404: From a9eafbdcbeb5d8b1058f42491ee42c7875345b35 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 20:53:22 +0100 Subject: [PATCH 09/19] cs fix --- tests/functional/test_workspaces.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index e27df27..882d87f 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -2,7 +2,6 @@ import os import snowflake.connector import tempfile -import time import unittest import warnings @@ -121,7 +120,7 @@ def test_load_files_invalid_workspace(self): workspace = self.workspaces.create() self.workspace_id = workspace['id'] try: - job = self.workspaces.load_files( + self.workspaces.load_files( workspace, {'tags': ['sapi-client-pythen-tests'], 'destination': 'data/in/files'} ) From 034d92dceba9715d55435ffb0881b4078ef3ecd9 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 21:55:05 +0100 Subject: [PATCH 10/19] update to python 3.8 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cf144fb..71af2b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.6 +FROM python:3.8 WORKDIR /code COPY . /code/ From f9fcfb6688ec30b8d2d1c7713829a691dda008f3 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Mon, 15 Mar 2021 21:57:07 +0100 Subject: [PATCH 11/19] close the connection --- tests/functional/test_workspaces.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 882d87f..d3a5fbe 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -22,7 +22,7 @@ def setUp(self): self.tables = Tables(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) self.files = Files(os.getenv('KBC_TEST_API_URL'), os.getenv('KBC_TEST_TOKEN')) try: - file_list = self.files.list(tags=['sapi-client-pythen-tests']) + file_list = self.files.list(tags=['sapi-client-python-tests']) for file in file_list: self.files.delete(file['id']) except exceptions.HTTPError as e: @@ -83,12 +83,14 @@ def test_load_tables_to_workspace(self): {table1_id: 'destination_1', table2_id: 'destination_2'} ) self.jobs.block_until_completed(job['id']) + conn = self.__get_snowflake_conenction(workspace['connection']) cursor = conn.cursor() cursor.execute('SELECT * FROM "destination_1"') self.assertEqual(('ping', 'pong'), cursor.fetchone()) cursor.execute('SELECT * FROM "destination_2"') self.assertEqual(('king', 'kong'), cursor.fetchone()) + conn.close() # test load files into an abs workspace def test_load_files_from_workspace(self): @@ -97,14 +99,15 @@ def test_load_files_from_workspace(self): # put a test file to storage file, path = tempfile.mkstemp(prefix='sapi-test') os.write(file, bytes('fooBar', 'utf-8')) - file_id = self.files.upload_file(path, tags=['sapi-client-pythen-tests', 'file1']) + os.close(file) + file_id = self.files.upload_file(path, tags=['sapi-client-python-tests', 'file1']) # create a workspace and load the file to it workspace = self.workspaces.create('abs') self.workspace_id = workspace['id'] job = self.workspaces.load_files( workspace, - {'tags': ['sapi-client-pythen-tests'], 'destination': 'data/in/files'} + {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'} ) self.jobs.block_until_completed(job['id']) @@ -122,7 +125,7 @@ def test_load_files_invalid_workspace(self): try: self.workspaces.load_files( workspace, - {'tags': ['sapi-client-pythen-tests'], 'destination': 'data/in/files'} + {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'} ) self.assertFail() except Exception as exception: From b376f289e2c3c900a5f065b44b2f4877f2242afb Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 11:31:36 +0100 Subject: [PATCH 12/19] change arg to workspace_id --- kbcstorage/workspaces.py | 4 ++-- tests/functional/test_workspaces.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index 5312d0e..89a0754 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -146,8 +146,8 @@ def load_tables(self, workspace_id, table_mapping, preserve=None): return self._post(url, data=body) # only supports abs workspace - # the workspace must be the object returned by the create method otherwise it won't have the connectionString - def load_files(self, workspace, file_mapping, preserve=None): + def load_files(self, workspace_id, file_mapping, preserve=None): + workspace = self.detail(workspace_id) if (workspace['type'] != 'file' and workspace['connection']['backend'] != 'abs'): raise Exception('Loading files to workspace is only available for ABS workspaces') files = Files(self.root_url, self.token) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index d3a5fbe..bafc5a4 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -2,6 +2,7 @@ import os import snowflake.connector import tempfile +import time import unittest import warnings @@ -106,7 +107,7 @@ def test_load_files_from_workspace(self): workspace = self.workspaces.create('abs') self.workspace_id = workspace['id'] job = self.workspaces.load_files( - workspace, + workspace['id'], {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'} ) self.jobs.block_until_completed(job['id']) From b1787569dc76de1cb9a98c79d598ce75b83ae702 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 12:17:27 +0100 Subject: [PATCH 13/19] use make_body --- kbcstorage/workspaces.py | 18 +++++------------- tests/functional/test_workspaces.py | 2 +- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index 89a0754..f7899ed 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -10,7 +10,7 @@ from kbcstorage.files import Files -def _make_body(mapping): +def _make_body(mapping, source_key='source'): """ Given a dict mapping Keboola tables to aliases, construct the body of the HTTP request to load said tables. @@ -23,7 +23,7 @@ def _make_body(mapping): body = {} template = 'input[{0}][{1}]' for i, (k, v) in enumerate(mapping.items()): - body[template.format(i, 'source')] = k + body[template.format(i, source_key)] = k body[template.format(i, 'destination')] = v return body @@ -152,18 +152,10 @@ def load_files(self, workspace_id, file_mapping, preserve=None): raise Exception('Loading files to workspace is only available for ABS workspaces') files = Files(self.root_url, self.token) file_list = files.list(tags=file_mapping['tags']) - inputs = [] + inputs = {} for file in file_list: - inputs.append({ - 'dataFileId': file['id'], - 'destination': file_mapping['destination'] - }) - body = {} - i = 0 - for input in inputs: - body['input[%s][dataFileId]' % (i)] = input['dataFileId'] - body['input[%s][destination]' % (i)] = input['destination'] - i += 1 + inputs[file['id']] = file_mapping['destination'] + body = _make_body(inputs, source_key='dataFileId') body['preserve'] = preserve url = '{}/{}/load'.format(self.base_url, workspace['id']) return self._post(url, data=body) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index bafc5a4..b6b4310 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -125,7 +125,7 @@ def test_load_files_invalid_workspace(self): self.workspace_id = workspace['id'] try: self.workspaces.load_files( - workspace, + workspace['id'], {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'} ) self.assertFail() From 436ad263faac560cb3e6dd6a27f2c65cecc11f8f Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 12:36:15 +0100 Subject: [PATCH 14/19] remove snflk dependency --- Dockerfile | 2 +- tests/functional/test_workspaces.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71af2b1..961886a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,6 @@ FROM python:3.8 WORKDIR /code COPY . /code/ -RUN pip3 install --no-cache-dir flake8 responses snowflake-connector-python +RUN pip3 install --no-cache-dir flake8 responses RUN python setup.py install ENTRYPOINT ["python"] diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index b6b4310..9b5889e 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -85,16 +85,19 @@ def test_load_tables_to_workspace(self): ) self.jobs.block_until_completed(job['id']) - conn = self.__get_snowflake_conenction(workspace['connection']) - cursor = conn.cursor() - cursor.execute('SELECT * FROM "destination_1"') - self.assertEqual(('ping', 'pong'), cursor.fetchone()) - cursor.execute('SELECT * FROM "destination_2"') - self.assertEqual(('king', 'kong'), cursor.fetchone()) - conn.close() + job = self.tables.create_raw( + bucket_id, + 'back-and-forth-table', + data_workspace_id=workspace['id'], + data_table_name='destination_1' + ) + self.jobs.block_until_completed(job['id']) + + new_table = self.tables.detail(bucket_id + '.back-and-forth-table') + self.assertEqual('back-and-forth-table', new_table['name']) # test load files into an abs workspace - def test_load_files_from_workspace(self): + def test_load_files_to_workspace(self): if (os.getenv('SKIP_ABS_TEST')): self.skipTest('Skipping ABS test because env var SKIP_ABS_TESTS was set') # put a test file to storage @@ -128,7 +131,7 @@ def test_load_files_invalid_workspace(self): workspace['id'], {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'} ) - self.assertFail() + self.fail('Should throw invalid workspace error') except Exception as exception: self.assertEqual('Loading files to workspace is only available for ABS workspaces', str(exception)) From 98d93b2b769b6ed3dfad21cd7498debab8a40b96 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 12:39:56 +0100 Subject: [PATCH 15/19] add doc to load_files --- kbcstorage/workspaces.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index f7899ed..46af614 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -147,6 +147,18 @@ def load_tables(self, workspace_id, table_mapping, preserve=None): # only supports abs workspace def load_files(self, workspace_id, file_mapping, preserve=None): + """ + Load tabes from storage into a workspace. + + Args: + workspace_id (int or str): The id of the workspace to which to load + the tables. + file_mapping (:obj:`dict`): contains tags: [], destination: string + preserve (bool): If False, drop files, else keep files in workspace. + + Raises: + requests.HTTPError: If the API request fails. + """ workspace = self.detail(workspace_id) if (workspace['type'] != 'file' and workspace['connection']['backend'] != 'abs'): raise Exception('Loading files to workspace is only available for ABS workspaces') From 673d91404cfda02f715433a47028530685c3708d Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 13:15:46 +0100 Subject: [PATCH 16/19] shovel snow --- tests/functional/test_workspaces.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 9b5889e..4923e8a 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -1,6 +1,5 @@ import csv import os -import snowflake.connector import tempfile import time import unittest @@ -144,12 +143,3 @@ def __create_table(self, bucket_id, table_name, row): writer.writerow(row) return self.tables.create(name=table_name, file_path=path, bucket_id=bucket_id) - def __get_snowflake_conenction(self, connection_properties): - return snowflake.connector.connect( - user=connection_properties['user'], - password=connection_properties['password'], - account=connection_properties['host'].split('.snowflakecomputing')[0], - warehouse=connection_properties['warehouse'], - database=connection_properties['database'], - schema=connection_properties['schema'] - ) From 080c7d06007a0f9b9b521ea0b942fd84d7f0d41f Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 13:38:26 +0100 Subject: [PATCH 17/19] cs --- tests/functional/test_workspaces.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 4923e8a..4cc381a 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -1,7 +1,6 @@ import csv import os import tempfile -import time import unittest import warnings @@ -142,4 +141,3 @@ def __create_table(self, bucket_id, table_name, row): writer.writeheader() writer.writerow(row) return self.tables.create(name=table_name, file_path=path, bucket_id=bucket_id) - From 7b74c5dd1bdc3235af1bd5c7d027b56c710ed944 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Tue, 16 Mar 2021 17:45:53 +0100 Subject: [PATCH 18/19] move load files to invalid workspace test to mocks --- tests/functional/test_workspaces.py | 12 ------------ tests/mocks/test_workspaces.py | 19 +++++++++++++++++++ tests/mocks/workspace_responses.py | 2 ++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/tests/functional/test_workspaces.py b/tests/functional/test_workspaces.py index 4cc381a..706d104 100644 --- a/tests/functional/test_workspaces.py +++ b/tests/functional/test_workspaces.py @@ -121,18 +121,6 @@ def test_load_files_to_workspace(self): ) self.assertEqual('fooBar', blob_client.download_blob().readall().decode('utf-8')) - def test_load_files_invalid_workspace(self): - workspace = self.workspaces.create() - self.workspace_id = workspace['id'] - try: - self.workspaces.load_files( - workspace['id'], - {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'} - ) - self.fail('Should throw invalid workspace error') - except Exception as exception: - self.assertEqual('Loading files to workspace is only available for ABS workspaces', str(exception)) - def __create_table(self, bucket_id, table_name, row): file, path = tempfile.mkstemp(prefix='sapi-test') with open(path, 'w') as csv_file: diff --git a/tests/mocks/test_workspaces.py b/tests/mocks/test_workspaces.py index f9ccb45..fcebacd 100644 --- a/tests/mocks/test_workspaces.py +++ b/tests/mocks/test_workspaces.py @@ -217,3 +217,22 @@ def test_reset_password_for_inexistent_workspace(self): with self.assertRaises(HTTPError) as error_context: self.ws.reset_password(workspace_id) assert error_context.exception.args[0] == msg + + @responses.activate + def test_load_files_to_invalid_workspace(self): + """ + Raises exception when mock loading_files to invalid workspace + """ + msg = ('Loading files to workspace is only available for ABS workspaces') + responses.add( + responses.Response( + method='GET', + url='https://connection.keboola.com/v2/storage/workspaces/1', + json=detail_response + ) + ) + workspace_id = '1' + try: + self.ws.load_files(workspace_id, {'tags': ['sapi-client-python-tests'], 'destination': 'data/in/files'}) + except Exception as ex: + assert str(ex) == msg diff --git a/tests/mocks/workspace_responses.py b/tests/mocks/workspace_responses.py index f2742db..cfebf50 100644 --- a/tests/mocks/workspace_responses.py +++ b/tests/mocks/workspace_responses.py @@ -27,6 +27,7 @@ detail_response = { "id": 1, "name": "boring_wozniak", + "type": "table", "component": "wr-db", "configurationId": "aws-1", "created": "2016-05-17T11:11:20+0200", @@ -51,6 +52,7 @@ create_response = { "id": 234, "name": "boring_wozniak", + "type": "table", "component": "wr-db", "configurationId": "aws-1", "created": "2016-05-17T11:11:20+0200", From d5529f348a9b42f40719bd69b389a22d2844f091 Mon Sep 17 00:00:00 2001 From: pivnicek Date: Wed, 17 Mar 2021 09:05:24 +0100 Subject: [PATCH 19/19] move comment --- kbcstorage/workspaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kbcstorage/workspaces.py b/kbcstorage/workspaces.py index 46af614..d969396 100644 --- a/kbcstorage/workspaces.py +++ b/kbcstorage/workspaces.py @@ -145,10 +145,10 @@ def load_tables(self, workspace_id, table_mapping, preserve=None): return self._post(url, data=body) - # only supports abs workspace def load_files(self, workspace_id, file_mapping, preserve=None): """ Load tabes from storage into a workspace. + * only supports abs workspace Args: workspace_id (int or str): The id of the workspace to which to load