From b3b2a67925172e34d078b7e063ccd6489f3d4d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Tue, 13 Sep 2016 16:32:54 +0200 Subject: [PATCH 01/60] [CORE-1666] add info after hosting publish; add template command in sync which allows to create template for socket.yml --- syncano_cli/hosting/commands.py | 8 ++++++-- syncano_cli/sync/commands.py | 19 +++++++++++++++++++ syncano_cli/sync/templates/__init__.py | 1 + syncano_cli/sync/templates/syncano_yml.py | 23 +++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 syncano_cli/sync/templates/__init__.py create mode 100644 syncano_cli/sync/templates/syncano_yml.py diff --git a/syncano_cli/hosting/commands.py b/syncano_cli/hosting/commands.py index 0bb3a1d..b036337 100644 --- a/syncano_cli/hosting/commands.py +++ b/syncano_cli/hosting/commands.py @@ -31,7 +31,11 @@ def publish(ctx, directory): validate_publish(directory) domain = validate_domain() # prepared for user defined domains; - ctx.obj['hosting_commands'].publish(domain=domain, base_dir=directory) + hosting_commands = ctx.obj['hosting_commands'] + hosting_commands.publish(domain=domain, base_dir=directory) + click.echo("INFO: Your site published. Go to: https://{}.syncano.site".format( + hosting_commands.instance.name) + ) @hosting.command() @@ -61,7 +65,7 @@ def delete(ctx, path): if click.confirm('Do you want to remove whole hosting?'): hosting_commands.delete_hosting(domain=domain, path=path) else: - raise Abort('Deleting aborted.') + raise Abort() else: hosting_commands.delete_path(domain=domain, path=path) diff --git a/syncano_cli/sync/commands.py b/syncano_cli/sync/commands.py index 1c13c44..b3d3145 100644 --- a/syncano_cli/sync/commands.py +++ b/syncano_cli/sync/commands.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- from __future__ import print_function +import os import time import click +from click import Abort from syncano_cli.base.connection import create_connection, get_instance from syncano_cli.sync.project import Project +from syncano_cli.sync.templates.syncano_yml import syncano_yml from watchdog.observers import Observer from .watch import ProjectEventHandler @@ -94,6 +97,22 @@ def watch(context): observer.join() +@sync.command() +@click.pass_context +def template(context): + """ + Creates sample project file; + """ + if os.path.isfile(context.obj['file']): + confirm = click.confirm(u'Are you sure you want to overwrite syncano.yml file with template?') + if not confirm: + raise Abort() + + with open(context.obj['file'], 'wb') as fp: + fp.write(syncano_yml) + click.echo("INFO: Template syncano.yml file created.") + + def do_push(context, classes, scripts, all): scripts = scripts or None classes = classes or None diff --git a/syncano_cli/sync/templates/__init__.py b/syncano_cli/sync/templates/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/syncano_cli/sync/templates/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/syncano_cli/sync/templates/syncano_yml.py b/syncano_cli/sync/templates/syncano_yml.py new file mode 100644 index 0000000..1151730 --- /dev/null +++ b/syncano_cli/sync/templates/syncano_yml.py @@ -0,0 +1,23 @@ + +syncano_yml = """ +classes: + book: + fields: + author: string + categories: array + holder: relation + pages: integer + premiere_date: datetime + title: string + used: boolean + group_permissions: create_objects + other_permissions: create_objects + user_profile: + fields: {} + group_permissions: create_objects + other_permissions: create_objects +scripts: [] +# - label: test +# runtime: python_library_v5.0 +# script: scripts/test.py +""" From 4cc11a4d91458c93ff13f51812b4d2176299a6d9 Mon Sep 17 00:00:00 2001 From: Mariusz Wisniewski Date: Tue, 13 Sep 2016 17:12:04 -0400 Subject: [PATCH 02/60] [skip ci] Info on default instance in login section --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index d081c00..9961533 100644 --- a/README.rst +++ b/README.rst @@ -53,6 +53,10 @@ The instance name will be set as default and used in all CLI commands. If you want to override this setting for a specific command, use --instance-name eg:: syncano sync --instance-name new-instance-1234 pull + +If you need to change default instance name, used for all future commands, use:: + + syncano default name_of_new_default_instance Documentation ============= From f154ee1677bfd7f9f570f90d4b7f64a6c12a4588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Thu, 15 Sep 2016 14:30:34 +0200 Subject: [PATCH 03/60] [CORE-1670] add possibility to handle endpoint metadatal --- syncano_cli/custom_sockets/formatters.py | 50 +++++++++++++++++++----- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index 486c968..d4842bf 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +import json import os +from collections import defaultdict import six import yaml @@ -68,9 +70,39 @@ def _json_process_endpoints(cls, endpoints): for name, endpoint_data in six.iteritems(endpoints): api_endpoints[name] = {'calls': cls._get_calls(endpoint_data)} + api_endpoints[name].update({'metadata': cls._get_metadata(endpoint_data)}) return api_endpoints + @classmethod + def _process_response_example(cls, metadata_key, inner_data): + if metadata_key not in cls.ENDPOINT_TYPES: + if metadata_key == 'response': + if 'example' in inner_data: + inner_data['example'] = json.loads(inner_data['example']) + + @classmethod + def _get_metadata(cls, endpoint_data): + metadata = defaultdict(dict) + for data_key, data in six.iteritems(endpoint_data): + if data_key in cls.HTTP_METHODS: + for metadata_key, inner_data in six.iteritems(data): + if metadata_key == 'parameters': + metadata[metadata_key][data_key] = inner_data + else: + cls._process_response_example(metadata_key, inner_data) + metadata[metadata_key] = inner_data + + elif data_key not in cls.ENDPOINT_TYPES: + if data_key == 'parameters': + metadata[metadata_key]['*'] = inner_data + else: + cls._process_response_example(data_key, data) + metadata[metadata_key] = inner_data + metadata[data_key] = data + + return metadata + @classmethod def _get_calls(cls, endpoint_data): calls = [] @@ -89,16 +121,16 @@ def _get_calls(cls, endpoint_data): }) elif type_or_method in cls.HTTP_METHODS: - if len(data) != 1: - raise OneEndpointPerMethodException() - for call_type, name in six.iteritems(data): - calls.append({ - 'type': call_type, - 'methods': [type_or_method], - 'name': name - }) + if call_type in cls.ENDPOINT_TYPES: + calls.append({ + 'type': call_type, + 'methods': [type_or_method], + 'name': name + }) + if len(calls) != 1: + raise OneEndpointPerMethodException() return calls @classmethod @@ -112,7 +144,7 @@ def _json_process_dependencies(cls, dependencies, directory): 'type': cls.DEPENDENCY_TYPES[dependencies_type], 'runtime_name': data['runtime_name'], 'name': dependency_name, - 'source': cls._get_source(data['file'], directory) + 'source': cls._get_source(data['file'], directory), }) return api_dependencies From bc47380547141d104c5280b122dcdd0add8a1351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Thu, 15 Sep 2016 14:56:43 +0200 Subject: [PATCH 04/60] [CORE-1670] remove exception about call object validation; --- syncano_cli/custom_sockets/exceptions.py | 4 ---- syncano_cli/custom_sockets/formatters.py | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/syncano_cli/custom_sockets/exceptions.py b/syncano_cli/custom_sockets/exceptions.py index 7f7a294..ad2447c 100644 --- a/syncano_cli/custom_sockets/exceptions.py +++ b/syncano_cli/custom_sockets/exceptions.py @@ -23,10 +23,6 @@ class ConfigNameMissingException(CLIBaseException): default_message = u'Variable name should be provided in config.' -class OneEndpointPerMethodException(CLIBaseException): - default_message = u'Only one endpoint per method allowed.' - - class BadYAMLDefinitionInEndpointsException(CLIBaseException): default_message = u'Specify one general endpoint or specify endpoints for each method.' diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index d4842bf..e9bb39a 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -5,7 +5,7 @@ import six import yaml -from syncano_cli.custom_sockets.exceptions import BadYAMLDefinitionInEndpointsException, OneEndpointPerMethodException +from syncano_cli.custom_sockets.exceptions import BadYAMLDefinitionInEndpointsException from syncano_cli.sync.scripts import ALLOWED_RUNTIMES @@ -129,8 +129,6 @@ def _get_calls(cls, endpoint_data): 'methods': [type_or_method], 'name': name }) - if len(calls) != 1: - raise OneEndpointPerMethodException() return calls @classmethod From d894e87a1a388f3d37a132af42a6a5e5c17d2e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Thu, 15 Sep 2016 15:17:55 +0200 Subject: [PATCH 05/60] [CORE-1670] correct metadata creation; --- syncano_cli/custom_sockets/formatters.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index e9bb39a..44b1c89 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -87,6 +87,8 @@ def _get_metadata(cls, endpoint_data): for data_key, data in six.iteritems(endpoint_data): if data_key in cls.HTTP_METHODS: for metadata_key, inner_data in six.iteritems(data): + if metadata_key in cls.ENDPOINT_TYPES: + continue if metadata_key == 'parameters': metadata[metadata_key][data_key] = inner_data else: From f4bcf88bab446f384aec4ca5f261e7241edf62b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 14:11:30 +0200 Subject: [PATCH 06/60] [CORE-1670] metadata handling; --- syncano_cli/custom_sockets/formatters.py | 21 ++++++++++++++++++--- syncano_cli/execute/utils.py | 3 ++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index 44b1c89..b37b005 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -79,7 +79,13 @@ def _process_response_example(cls, metadata_key, inner_data): if metadata_key not in cls.ENDPOINT_TYPES: if metadata_key == 'response': if 'example' in inner_data: - inner_data['example'] = json.loads(inner_data['example']) + if isinstance(inner_data, dict): + inner_data['example'] = inner_data + if isinstance(inner_data, six.string_types): + try: + inner_data['example'] = json.loads(inner_data['example']) + except (TypeError, ValueError): + inner_data['example'] = inner_data['example'] @classmethod def _get_metadata(cls, endpoint_data): @@ -97,10 +103,10 @@ def _get_metadata(cls, endpoint_data): elif data_key not in cls.ENDPOINT_TYPES: if data_key == 'parameters': - metadata[metadata_key]['*'] = inner_data + metadata[data_key]['*'] = data else: cls._process_response_example(data_key, data) - metadata[metadata_key] = inner_data + metadata[data_key] = data metadata[data_key] = data return metadata @@ -158,8 +164,17 @@ def _yml_process_endpoints(cls, endpoints): yml_endpoints = {} for endpoint_name, endpoint_data in six.iteritems(endpoints): yml_endpoints[endpoint_name] = cls._yml_process_calls(endpoint_data['calls']) + yml_endpoints.update(cls._yml_process_metadata(endpoint_data)) return yml_endpoints + @classmethod + def _yml_process_metadata(cls, endpoint_data): + metadata = endpoint_data['metadata'] + if 'response' in metadata and 'example' in metadata['response']: + t = json.dumps(metadata['response']['example'], indent=4) + metadata['response']['example'] = metadata['response']['example'] + return endpoint_data['metadata'] + @classmethod def _yml_process_calls(cls, data_calls): calls = {} diff --git a/syncano_cli/execute/utils.py b/syncano_cli/execute/utils.py index 33c9a0a..404421d 100644 --- a/syncano_cli/execute/utils.py +++ b/syncano_cli/execute/utils.py @@ -2,6 +2,7 @@ from __future__ import print_function import json +import six def print_response(response): @@ -16,7 +17,7 @@ def print_response(response): def _print_result(result): try: - if type(result) in [str, unicode]: + if isinstance(type, six.string_types): output = json.loads(result) else: output = result From a1a17d044eb828e8c2b9d71fb291764dbc1946d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 14:14:29 +0200 Subject: [PATCH 07/60] [CORE-1670] correct flake and isort issues; --- syncano_cli/custom_sockets/formatters.py | 4 ---- syncano_cli/execute/utils.py | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index b37b005..7015312 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -169,10 +169,6 @@ def _yml_process_endpoints(cls, endpoints): @classmethod def _yml_process_metadata(cls, endpoint_data): - metadata = endpoint_data['metadata'] - if 'response' in metadata and 'example' in metadata['response']: - t = json.dumps(metadata['response']['example'], indent=4) - metadata['response']['example'] = metadata['response']['example'] return endpoint_data['metadata'] @classmethod diff --git a/syncano_cli/execute/utils.py b/syncano_cli/execute/utils.py index 404421d..3cc3fc6 100644 --- a/syncano_cli/execute/utils.py +++ b/syncano_cli/execute/utils.py @@ -2,6 +2,7 @@ from __future__ import print_function import json + import six From 4acb4ab1f5c15a234cb18f3b1d8c0da524f66f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 14:18:33 +0200 Subject: [PATCH 08/60] [CORE-1670] correct example in metadata handling; --- syncano_cli/custom_sockets/formatters.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index 7015312..b20ccc9 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -79,9 +79,9 @@ def _process_response_example(cls, metadata_key, inner_data): if metadata_key not in cls.ENDPOINT_TYPES: if metadata_key == 'response': if 'example' in inner_data: - if isinstance(inner_data, dict): - inner_data['example'] = inner_data - if isinstance(inner_data, six.string_types): + if isinstance(inner_data['example'], dict): + inner_data['example'] = inner_data['example'] + elif isinstance(inner_data['example'], six.string_types): try: inner_data['example'] = json.loads(inner_data['example']) except (TypeError, ValueError): From 2dc972b436579f3f6c5fb72b1bd0e4735081b652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 14:24:57 +0200 Subject: [PATCH 09/60] [CORE-1670] correct tests - instance name format changed; --- tests/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/base.py b/tests/base.py index 412c1c7..fb6f2ab 100644 --- a/tests/base.py +++ b/tests/base.py @@ -48,7 +48,7 @@ def setUpClass(cls): super(InstanceMixin, cls).setUpClass() cls.instance = cls.connection.Instance.please.create( - name='test_cli_i%s' % cls.generate_hash()[:10], + name='test-cli-i%s' % cls.generate_hash()[:10], description='IntegrationTest %s' % datetime.now(), ) From a529bdc6ce439447de4096e189fdc8173d56a41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 14:34:09 +0200 Subject: [PATCH 10/60] [CORE-1670] correct getting metadata (old socket have no such field) --- syncano_cli/custom_sockets/formatters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index b20ccc9..298505d 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -169,7 +169,7 @@ def _yml_process_endpoints(cls, endpoints): @classmethod def _yml_process_metadata(cls, endpoint_data): - return endpoint_data['metadata'] + return endpoint_data.get('metadata', {}) # some old custom sockets do not have this field; @classmethod def _yml_process_calls(cls, data_calls): From 105e66ea0137760364e9fc366b85f5a0459844fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 14:41:13 +0200 Subject: [PATCH 11/60] [CORE-1670] correct print_result function; --- syncano_cli/execute/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncano_cli/execute/utils.py b/syncano_cli/execute/utils.py index 3cc3fc6..f02351f 100644 --- a/syncano_cli/execute/utils.py +++ b/syncano_cli/execute/utils.py @@ -18,7 +18,7 @@ def print_response(response): def _print_result(result): try: - if isinstance(type, six.string_types): + if isinstance(result, six.string_types): output = json.loads(result) else: output = result From d884c2d77319b4a9666082e21e1cb36617d7d474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 15:14:31 +0200 Subject: [PATCH 12/60] [CORE-1672] add python3 tests: tox; --- circle.yml | 9 ++++----- requirements-tests.txt | 1 + run_tests.sh | 8 ++++++++ tox.ini | 4 ++++ 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 run_tests.sh create mode 100644 tox.ini diff --git a/circle.yml b/circle.yml index cab6c0c..37d7016 100644 --- a/circle.yml +++ b/circle.yml @@ -5,14 +5,13 @@ machine: dependencies: pre: - pip install -U setuptools + - pip install -r requirements_tests.txt + post: + - pyenv local 3.4.3 2.7.6 test: override: - - pip install -r requirements-tests.txt - - flake8 . - - isort --recursive --check-only . - - python setup.py check -s --restructuredtext - - python setup.py test + - tox deployment: production: diff --git a/requirements-tests.txt b/requirements-tests.txt index 5dcf92a..52865d9 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -1,3 +1,4 @@ +tox==2.3.1 flake8==2.4.1 isort==4.0.0 mock==1.3.0 diff --git a/run_tests.sh b/run_tests.sh new file mode 100644 index 0000000..5917669 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +flake8 . +isort --recursive --check-only . + +python setup.py test diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..d88bb83 --- /dev/null +++ b/tox.ini @@ -0,0 +1,4 @@ +[tox] +envlist = py27,py34 +[testenv] +commands=./run_tests.sh \ No newline at end of file From 6a12502d3b836eaaf991178e0b7692d75aa56c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 15:17:37 +0200 Subject: [PATCH 13/60] [CORE-1672] add python3 tests: tox - add env variables; --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index d88bb83..26f3174 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,5 @@ [tox] envlist = py27,py34 [testenv] +passenv = INTEGRATION_API_ROOT INTEGRATION_API_KEY INTEGRATION_API_EMAIL INTEGRATION_API_PASSWORD INTEGRATION_INSTANCE_NAME INTEGRATION_USER_NAME INTEGRATION_USER_PASSWORD SYNCANO_APIROOT SYNCANO_EMAIL SYNCANO_PASSWORD commands=./run_tests.sh \ No newline at end of file From 012e02dfb4ed8ee21ade93edeee105594f5669b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 15:24:25 +0200 Subject: [PATCH 14/60] [CORE-1672] add python3 tests: correct yml; --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 37d7016..a8ae165 100644 --- a/circle.yml +++ b/circle.yml @@ -5,7 +5,7 @@ machine: dependencies: pre: - pip install -U setuptools - - pip install -r requirements_tests.txt + - pip install -r requirements-tests.txt post: - pyenv local 3.4.3 2.7.6 From 8fb32b5ba6c5fcb22e2e79025fe6242c3253de69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 15:36:10 +0200 Subject: [PATCH 15/60] [CORE-1672] add python3 tests: correct yml; --- circle.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/circle.yml b/circle.yml index a8ae165..ceb8bc5 100644 --- a/circle.yml +++ b/circle.yml @@ -6,8 +6,6 @@ dependencies: pre: - pip install -U setuptools - pip install -r requirements-tests.txt - post: - - pyenv local 3.4.3 2.7.6 test: override: From 6498f2a1238fc83edce074b0a1b0e2989dc9f7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 15:43:57 +0200 Subject: [PATCH 16/60] [CORE-1672] add python3 tests: make run_tests executable; --- run_tests.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 run_tests.sh diff --git a/run_tests.sh b/run_tests.sh old mode 100644 new mode 100755 From a8e8f376ed242d1f29163f85c62888f031bb31d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 15:57:37 +0200 Subject: [PATCH 17/60] [CORE-1672] add python3 tests: skip .tox directory from isort; --- .isort.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.isort.cfg b/.isort.cfg index fbbfe22..7129db3 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,4 +2,4 @@ line_length=120 multi_line_output=3 default_section=THIRDPARTY -skip=.eggs,egg-info,builds,dist,dev.py +skip=.eggs,egg-info,builds,dist,dev.py,.tox From 0c7c3890825548e8a2d601d45025b86bcbf36154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 16:12:18 +0200 Subject: [PATCH 18/60] [CORE-1672] add python3 tests: correct circle test run; --- circle.yml | 1 + tox.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/circle.yml b/circle.yml index ceb8bc5..b6e1613 100644 --- a/circle.yml +++ b/circle.yml @@ -9,6 +9,7 @@ dependencies: test: override: + - python setup.py check -s --restructuredtext - tox deployment: diff --git a/tox.ini b/tox.ini index 26f3174..e6dea72 100644 --- a/tox.ini +++ b/tox.ini @@ -2,4 +2,5 @@ envlist = py27,py34 [testenv] passenv = INTEGRATION_API_ROOT INTEGRATION_API_KEY INTEGRATION_API_EMAIL INTEGRATION_API_PASSWORD INTEGRATION_INSTANCE_NAME INTEGRATION_USER_NAME INTEGRATION_USER_PASSWORD SYNCANO_APIROOT SYNCANO_EMAIL SYNCANO_PASSWORD +deps= -rrequirements-tests.txt commands=./run_tests.sh \ No newline at end of file From 65f2aac4b4c1f4b5edae633206397a206794507c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 16:21:29 +0200 Subject: [PATCH 19/60] [CORE-1672] add python3 tests: correct circle test run; --- circle.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/circle.yml b/circle.yml index b6e1613..6bcd769 100644 --- a/circle.yml +++ b/circle.yml @@ -6,6 +6,8 @@ dependencies: pre: - pip install -U setuptools - pip install -r requirements-tests.txt + post: + - pyenv local 3.4.3 2.7.6 test: override: From 97cc691c9326cb99808a254cb85d5a7ea242c675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 16:37:57 +0200 Subject: [PATCH 20/60] [CORE-1672] add python3 tests: correct circle test run; --- circle.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 6bcd769..a5440c8 100644 --- a/circle.yml +++ b/circle.yml @@ -1,13 +1,10 @@ -machine: - python: - version: 2.7.5 dependencies: pre: - pip install -U setuptools - pip install -r requirements-tests.txt post: - - pyenv local 3.4.3 2.7.6 + - pyenv local 3.4.3 2.7.10 test: override: From 3309aadb24920b11dd4358f6dd9cbd3762af5af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 16:55:04 +0200 Subject: [PATCH 21/60] [CORE-1672] Python3 related changes; --- circle.yml | 3 +++ syncano_cli/base/connection.py | 11 +++++++++-- syncano_cli/config.py | 10 +++++++++- syncano_cli/instance/command.py | 11 +++++++++-- syncano_cli/parse_to_syncano/commands.py | 6 +++--- syncano_cli/parse_to_syncano/moses.py | 3 ++- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/circle.yml b/circle.yml index a5440c8..2f63c54 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,6 @@ +machine: + python: + version: 2.7.10 dependencies: pre: diff --git a/syncano_cli/base/connection.py b/syncano_cli/base/connection.py index db275c8..efd2aea 100644 --- a/syncano_cli/base/connection.py +++ b/syncano_cli/base/connection.py @@ -1,11 +1,18 @@ # -*- coding: utf-8 -*- -from ConfigParser import NoOptionError, NoSectionError - +import six import syncano from syncano.exceptions import SyncanoException from syncano_cli.base.exceptions import BadCredentialsException, InstanceNotFoundException from syncano_cli.config import ACCOUNT_CONFIG, ACCOUNT_CONFIG_PATH +if six.PY2: + from ConfigParser import NoOptionError, NoSectionError +elif six.PY3: + from configparser import NoOptionError, NoSectionError +else: + raise ImportError() + + def get_instance_name(config, instance_name): ACCOUNT_CONFIG.read(config) diff --git a/syncano_cli/config.py b/syncano_cli/config.py index 732168f..598ff44 100644 --- a/syncano_cli/config.py +++ b/syncano_cli/config.py @@ -1,7 +1,15 @@ # -*- coding: UTF=8 -*- import os -from ConfigParser import ConfigParser + +import six + +if six.PY2: + from ConfigParser import ConfigParser +elif six.PY3: + from configparser import ConfigParser +else: + raise ImportError() ACCOUNT_CONFIG_PATH = os.path.join(os.path.expanduser('~'), '.syncano') ACCOUNT_CONFIG = ConfigParser() diff --git a/syncano_cli/instance/command.py b/syncano_cli/instance/command.py index f6409ca..6c68a7b 100644 --- a/syncano_cli/instance/command.py +++ b/syncano_cli/instance/command.py @@ -1,9 +1,16 @@ # -*- coding: utf-8 -*- -from ConfigParser import NoOptionError - +import six from syncano_cli.base.command import BaseConnectionCommand from syncano_cli.config import ACCOUNT_CONFIG +if six.PY2: + from ConfigParser import NoOptionError +elif six.PY3: + from configparser import NoOptionError +else: + raise ImportError() + + class InstanceCommands(BaseConnectionCommand): diff --git a/syncano_cli/parse_to_syncano/commands.py b/syncano_cli/parse_to_syncano/commands.py index 606a53c..69d5f26 100644 --- a/syncano_cli/parse_to_syncano/commands.py +++ b/syncano_cli/parse_to_syncano/commands.py @@ -32,14 +32,14 @@ def parse(context): check_configuration(config, silent=True) application_id = config.get('P2S', 'PARSE_APPLICATION_ID') instance_name = config.get('P2S', 'SYNCANO_INSTANCE_NAME') - confirmation = raw_input( + confirmation = click.confirm( 'Are you sure you want to copy your data from Parse application ({application_id})' ' to the Syncano Instance ({instance_name})? Y/N [Y]: '.format( application_id=application_id, instance_name=instance_name) - ) or 'Y' + ) - if confirmation not in ['Y', 'YES', 'y', 'yes']: + if not confirmation: click.echo('INFO: Transfer aborted.') return diff --git a/syncano_cli/parse_to_syncano/moses.py b/syncano_cli/parse_to_syncano/moses.py index d236d49..c455ebe 100755 --- a/syncano_cli/parse_to_syncano/moses.py +++ b/syncano_cli/parse_to_syncano/moses.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- +import click from syncano_cli.config import ACCOUNT_CONFIG_PATH from syncano_cli.parse_to_syncano.config import CONFIG_VARIABLES_NAMES def force_config_value(config, config_var_name, section='P2S'): - config_var = raw_input('{}: '.format(config_var_name)) + config_var = click.prompt('{}: '.format(config_var_name)) if not config.has_section(section): config.add_section(section) config.set(section, config_var_name, config_var) From 04fe3132ce4482ff9741b80c5e034f6fef155af6 Mon Sep 17 00:00:00 2001 From: Adam Wardecki Date: Fri, 16 Sep 2016 17:01:08 +0200 Subject: [PATCH 22/60] Improve docstrings --- syncano_cli/base/exceptions.py | 4 ++-- syncano_cli/custom_sockets/commands.py | 2 +- syncano_cli/instance/commands.py | 4 +++- syncano_cli/parse_to_syncano/commands.py | 6 +++--- syncano_cli/sync/commands.py | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/syncano_cli/base/exceptions.py b/syncano_cli/base/exceptions.py index 74fdfe8..b204bd2 100644 --- a/syncano_cli/base/exceptions.py +++ b/syncano_cli/base/exceptions.py @@ -23,11 +23,11 @@ class JSONParseException(CLIBaseException): class BadCredentialsException(CLIBaseException): - default_message = u'Wrong credential provided when login.' + default_message = u'Wrong login credentials provided.' class NotLoggedInException(CLIBaseException): - default_message = u'Do a login first: `syncano login`.' + default_message = u'Please log in to your account: `syncano login`.' class InstanceNotFoundException(CLIBaseException): diff --git a/syncano_cli/custom_sockets/commands.py b/syncano_cli/custom_sockets/commands.py index 659c6e7..11bb864 100644 --- a/syncano_cli/custom_sockets/commands.py +++ b/syncano_cli/custom_sockets/commands.py @@ -18,7 +18,7 @@ def top_sockets(): @click.option('--instance-name', help=u'Instance name.') def sockets(ctx, config, instance_name): """ - Allow to create a custom socket. + Create and manage custom sockets. """ instance = get_instance(config, instance_name) socket_command = SocketCommand(instance=instance) diff --git a/syncano_cli/instance/commands.py b/syncano_cli/instance/commands.py index 1baa320..fab87bf 100644 --- a/syncano_cli/instance/commands.py +++ b/syncano_cli/instance/commands.py @@ -18,7 +18,9 @@ def top_instance(): @click.option('--instance-name', help=u'Instance name.') def instances(ctx, config, instance_name): """ - Handle hosting and hosting files. Allow to publish static pages to the Syncano Hosting. + Mangage your Instances. Instance is an equivalent of a project or a set of data. + It contains information about Sockets, Data Classes, Data Objects and more. + You can own and/or belong to multiple instances. """ connection = create_connection(config, instance_name) instance_commands = InstanceCommands(connection) diff --git a/syncano_cli/parse_to_syncano/commands.py b/syncano_cli/parse_to_syncano/commands.py index 606a53c..1dbd880 100644 --- a/syncano_cli/parse_to_syncano/commands.py +++ b/syncano_cli/parse_to_syncano/commands.py @@ -16,7 +16,7 @@ def top_migrate(): @click.option('--config', help=u'Account configuration file.') def migrate(context, config): """ - Command for transfer data to Syncano + Migrate Parse data to Syncano """ config = config or ACCOUNT_CONFIG_PATH context.obj['config'] = config @@ -26,7 +26,7 @@ def migrate(context, config): @click.pass_context def parse(context): """ - Synchronize the parse data object with syncano data objects; + Synchronize the Parse data objects with Syncano data objects; """ config = read_config(config_path=context.obj['config']) check_configuration(config, silent=True) @@ -53,7 +53,7 @@ def parse(context): @click.option('--force', is_flag=True, default=False, help="Force to overwrite previous config.") def configure(context, current, force): """ - Configure the data needed for connection to the parse and syncano; + Configure the data needed for connection between Parse and Syncano; """ config = read_config(config_path=context.obj['config']) if current: diff --git a/syncano_cli/sync/commands.py b/syncano_cli/sync/commands.py index 1c13c44..e90113b 100644 --- a/syncano_cli/sync/commands.py +++ b/syncano_cli/sync/commands.py @@ -23,7 +23,7 @@ def top_sync(): @click.option('--instance-name', help=u'Instance name.') def sync(context, file, config, instance_name): """ - Command for syncing data - classes and scripts + Sync your scripts and data classes. :param context: :param file: file which will be used for syncing :param config: the config path - the cli config will be stored there From 535ab24e0f2c3161bb6f1a15894abd6f770e88c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 17:04:06 +0200 Subject: [PATCH 23/60] [CORE-1672] flake8 issues; --- syncano_cli/base/connection.py | 1 - syncano_cli/instance/command.py | 1 - 2 files changed, 2 deletions(-) diff --git a/syncano_cli/base/connection.py b/syncano_cli/base/connection.py index efd2aea..f5a157a 100644 --- a/syncano_cli/base/connection.py +++ b/syncano_cli/base/connection.py @@ -13,7 +13,6 @@ raise ImportError() - def get_instance_name(config, instance_name): ACCOUNT_CONFIG.read(config) try: diff --git a/syncano_cli/instance/command.py b/syncano_cli/instance/command.py index 6c68a7b..6c10dc7 100644 --- a/syncano_cli/instance/command.py +++ b/syncano_cli/instance/command.py @@ -11,7 +11,6 @@ raise ImportError() - class InstanceCommands(BaseConnectionCommand): def list(self): From f10ca7681d5be541c9124692f38ef1edff28ad81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 17:10:28 +0200 Subject: [PATCH 24/60] [CORE-1672] add input to test_configure; --- syncano_cli/parse_to_syncano/moses.py | 2 +- tests/test_migrate_commands.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/syncano_cli/parse_to_syncano/moses.py b/syncano_cli/parse_to_syncano/moses.py index c455ebe..d69167a 100755 --- a/syncano_cli/parse_to_syncano/moses.py +++ b/syncano_cli/parse_to_syncano/moses.py @@ -5,7 +5,7 @@ def force_config_value(config, config_var_name, section='P2S'): - config_var = click.prompt('{}: '.format(config_var_name)) + config_var = click.prompt('{}'.format(config_var_name)) if not config.has_section(section): config.add_section(section) config.set(section, config_var_name, config_var) diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index cbf2db0..4eee1b7 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -32,7 +32,7 @@ def test_configure(self): result = self.runner.invoke(cli, args=['migrate', 'configure'], obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) - result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], obj={}) + result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], input='xxx\n', obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) result = self.runner.invoke(cli, args=['migrate', 'configure', '--current'], obj={}) From 2152ea226972210e934551d182e6ab478668933f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 17:11:15 +0200 Subject: [PATCH 25/60] [CORE-1672] add input to test_configure; --- tests/test_migrate_commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index 4eee1b7..891a082 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -32,7 +32,8 @@ def test_configure(self): result = self.runner.invoke(cli, args=['migrate', 'configure'], obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) - result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], input='xxx\n', obj={}) + result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], input='xxx\nxxx\nxxx\nxxx\n', + obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) result = self.runner.invoke(cli, args=['migrate', 'configure', '--current'], obj={}) From a9024b21a69c6c83686619522856d27b99a6347b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 17:16:41 +0200 Subject: [PATCH 26/60] [CORE-1672] another bunch of corrects: skip scripts in isort check; correct migrations tests; --- .isort.cfg | 2 +- tests/test_migrate_commands.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.isort.cfg b/.isort.cfg index 7129db3..818a57f 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,4 +2,4 @@ line_length=120 multi_line_output=3 default_section=THIRDPARTY -skip=.eggs,egg-info,builds,dist,dev.py,.tox +skip=.eggs,egg-info,builds,dist,dev.py,.tox,scripts/ diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index 891a082..77c495b 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -40,7 +40,7 @@ def test_configure(self): self.assertIn('PARSE_MASTER_KEY', result.output) def test_parse(self): - result = self.runner.invoke(cli, args=['migrate', 'parse'], obj={}) + result = self.runner.invoke(cli, args=['migrate', 'parse'], input='N\n', obj={}) self.assertIn('Aborted!', result.output) @mock.patch('syncano_cli.parse_to_syncano.parse.connection.ParseConnection.request') @@ -55,7 +55,7 @@ def test_class_migrations(self, request_mock): request_mock.return_value = json.loads(f.read()) self.assertFalse(request_mock.called) - self.runner.invoke(cli, args=['migrate', 'parse'], obj={}, input='Y') + self.runner.invoke(cli, args=['migrate', 'parse'], obj={}, input='y\n') self.assertTrue(request_mock.called) syncano_class = self.instance.classes.get(name='test_class_1234') From 8950a63fef11a36779365a3f799bc6b7b82d6db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 16 Sep 2016 17:22:16 +0200 Subject: [PATCH 27/60] [CORE-1672] skip scripts; --- .isort.cfg | 2 +- tests/test_migrate_commands.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.isort.cfg b/.isort.cfg index 818a57f..9eead09 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,4 +2,4 @@ line_length=120 multi_line_output=3 default_section=THIRDPARTY -skip=.eggs,egg-info,builds,dist,dev.py,.tox,scripts/ +skip=.eggs,egg-info,builds,dist,dev.py,.tox,scripts diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index 77c495b..6372984 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -41,7 +41,7 @@ def test_configure(self): def test_parse(self): result = self.runner.invoke(cli, args=['migrate', 'parse'], input='N\n', obj={}) - self.assertIn('Aborted!', result.output) + self.assertIn('Transfer aborted.', result.output) @mock.patch('syncano_cli.parse_to_syncano.parse.connection.ParseConnection.request') @mock.patch('syncano_cli.parse_to_syncano.migrations.transfer.SyncanoTransfer.process_relations', From 1da337a5a50c2644fd9bd0302e709698fc3686ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 10:47:35 +0200 Subject: [PATCH 28/60] [CORE-1672] correct tests; --- tests/__init__.py | 3 --- tests/test_migrate_commands.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index e675089..e69de29 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- -import test_sync_commands # noqa -import test_migrate_commands # noqa diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index 6372984..94cc74c 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -82,7 +82,7 @@ def test_object_migrations(self, request_mock): request_mock.side_effect = [classes, objects, {'results': []}, parse_installations] self.assertFalse(request_mock.called) - self.runner.invoke(cli, args=['migrate', 'parse'], obj={}, input='Y') + self.runner.invoke(cli, args=['migrate', 'parse'], obj={}, input='y\n') self.assertTrue(request_mock.called) objects_list = self.instance.classes.get(name='test_class_1234').objects.all() From 60f502af88ffff1cd41e22a4d297d768994503db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 11:06:34 +0200 Subject: [PATCH 29/60] [CORE-1672] change file read/write to text mode; --- syncano_cli/account/command.py | 2 +- syncano_cli/commands.py | 2 +- syncano_cli/hosting/command.py | 2 +- syncano_cli/instance/command.py | 2 +- syncano_cli/parse_to_syncano/moses.py | 2 +- syncano_cli/sync/commands.py | 2 +- syncano_cli/sync/project.py | 8 ++++---- syncano_cli/sync/scripts.py | 4 ++-- tests/base.py | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/syncano_cli/account/command.py b/syncano_cli/account/command.py index 1ac1ad5..dd61b5e 100644 --- a/syncano_cli/account/command.py +++ b/syncano_cli/account/command.py @@ -20,5 +20,5 @@ def register(self, email, password, first_name, last_name, invitation_key): ) ACCOUNT_CONFIG.set('DEFAULT', 'api_key', api_key) - with open(self.config_path, 'wb') as fp: + with open(self.config_path, 'wt') as fp: ACCOUNT_CONFIG.write(fp) diff --git a/syncano_cli/commands.py b/syncano_cli/commands.py index bd7b794..0bcc60b 100644 --- a/syncano_cli/commands.py +++ b/syncano_cli/commands.py @@ -33,7 +33,7 @@ def login(context, config, instance_name): ACCOUNT_CONFIG.set('DEFAULT', 'key', ACCOUNT_KEY) if instance_name: ACCOUNT_CONFIG.set('DEFAULT', 'instance_name', instance_name) - with open(context.obj['config'], 'wb') as fp: + with open(context.obj['config'], 'wt') as fp: ACCOUNT_CONFIG.write(fp) click.echo("INFO: Login successful.") except SyncanoException: diff --git a/syncano_cli/hosting/command.py b/syncano_cli/hosting/command.py index b415f0b..91a335b 100644 --- a/syncano_cli/hosting/command.py +++ b/syncano_cli/hosting/command.py @@ -42,7 +42,7 @@ def publish(self, domain, base_dir): self._validate_path(file_path) sys_path = os.path.join(folder, single_file) - with open(sys_path, 'rb') as upload_file: + with open(sys_path, 'rt') as upload_file: click.echo(u'INFO: Uploading file: {}'.format(file_path)) getattr(hosting, upload_method_name)(path=file_path, file=upload_file) diff --git a/syncano_cli/instance/command.py b/syncano_cli/instance/command.py index 6c10dc7..a3fae5a 100644 --- a/syncano_cli/instance/command.py +++ b/syncano_cli/instance/command.py @@ -30,7 +30,7 @@ def delete(self, instance_name): @classmethod def set_default(cls, instance_name, config_path): ACCOUNT_CONFIG.set('DEFAULT', 'instance_name', instance_name) - with open(config_path, 'wb') as fp: + with open(config_path, 'wt') as fp: ACCOUNT_CONFIG.write(fp) def create(self, instance_name, description): diff --git a/syncano_cli/parse_to_syncano/moses.py b/syncano_cli/parse_to_syncano/moses.py index d69167a..b6c6ccc 100755 --- a/syncano_cli/parse_to_syncano/moses.py +++ b/syncano_cli/parse_to_syncano/moses.py @@ -23,7 +23,7 @@ def check_config_value(config, config_var_name, silent, section='P2S'): def write_config_to_file(config): - with open(ACCOUNT_CONFIG_PATH, 'wb') as config_file: + with open(ACCOUNT_CONFIG_PATH, 'wt') as config_file: config.write(config_file) diff --git a/syncano_cli/sync/commands.py b/syncano_cli/sync/commands.py index b3d3145..fea6fba 100644 --- a/syncano_cli/sync/commands.py +++ b/syncano_cli/sync/commands.py @@ -108,7 +108,7 @@ def template(context): if not confirm: raise Abort() - with open(context.obj['file'], 'wb') as fp: + with open(context.obj['file'], 'wt') as fp: fp.write(syncano_yml) click.echo("INFO: Template syncano.yml file created.") diff --git a/syncano_cli/sync/project.py b/syncano_cli/sync/project.py index ea19654..f9d1167 100644 --- a/syncano_cli/sync/project.py +++ b/syncano_cli/sync/project.py @@ -22,7 +22,7 @@ def __init__(self, classes=None, scripts=None, timestamp=None, **kwargs): @classmethod def from_config(cls, config): try: - with open(config, 'rb') as fp: + with open(config, 'rt') as fp: cfg = yaml.safe_load(fp) cfg['timestamp'] = os.path.getmtime(config) except IOError: @@ -32,14 +32,14 @@ def from_config(cls, config): return project def write(self, config): - with open(config, 'wb') as fp: + with open(config, 'wt') as fp: fp.write(yaml.safe_dump({ 'classes': self.classes, 'scripts': self.scripts }, default_flow_style=False)) def write_json(self, config): - with open(config, 'wb') as fp: + with open(config, 'wt') as fp: json.dump({ 'classes': self.classes, 'scripts': self.scripts @@ -72,7 +72,7 @@ def push_to_instance(self, instance, all=False, classes=None, try: last_sync = os.path.getmtime('.sync') except OSError: - with open('.sync', 'wb'): # touch file + with open('.sync', 'wt'): # touch file pass last_sync = 0 diff --git a/syncano_cli/sync/scripts.py b/syncano_cli/sync/scripts.py index aa0b3db..a112fd8 100644 --- a/syncano_cli/sync/scripts.py +++ b/syncano_cli/sync/scripts.py @@ -83,7 +83,7 @@ def pull_scripts(instance, include): path = os.path.join('scripts', filename) - with open(path, 'wb') as script_file: + with open(path, 'wt') as script_file: script_file.write(script.source) script_info = { @@ -138,7 +138,7 @@ def push_scripts(instance, scripts, config_only=True): instance_name=instance.name, config={} ) - with open(s['script'], 'rb') as source: + with open(s['script'], 'rt') as source: remote_script.source = source.read() config = s.get('config', {}) diff --git a/tests/base.py b/tests/base.py index fb6f2ab..ffcf918 100644 --- a/tests/base.py +++ b/tests/base.py @@ -126,7 +126,7 @@ def get_script_path(self, unique): ) def modify_yml_file(self, key, object, operation='update'): - with open(self.yml_file, 'rb') as f: + with open(self.yml_file, 'rt') as f: yml_syncano = yaml.safe_load(f) if operation == 'update': yml_syncano[key].update(object) @@ -135,7 +135,7 @@ def modify_yml_file(self, key, object, operation='update'): else: raise Exception('not supported operation') - with open(self.yml_file, 'wb') as f: + with open(self.yml_file, 'wt') as f: f.write(yaml.safe_dump(yml_syncano, default_flow_style=False)) def create_syncano_class(self, unique): From fed4562a29502344bf1081e0adecf955a6fb8f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 12:30:36 +0200 Subject: [PATCH 30/60] [CORE-1672] change all iteritems to six.iteritems; --- syncano_cli/main.py | 2 +- syncano_cli/parse_to_syncano/migrations/relation.py | 8 +++++--- syncano_cli/parse_to_syncano/processors/klass.py | 5 +++-- .../processors/push_notifications.py | 4 +++- syncano_cli/sync/classes.py | 9 +++++---- syncano_cli/sync/scripts.py | 2 +- tests/test_account.py | 12 +++++------- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/syncano_cli/main.py b/syncano_cli/main.py index ea75362..3214ad9 100644 --- a/syncano_cli/main.py +++ b/syncano_cli/main.py @@ -34,4 +34,4 @@ def main(): except SyncanoException as e: raise SyncanoLibraryException(e.reason) except Exception as e: - raise CLIBaseException(e.message) + raise CLIBaseException(e) diff --git a/syncano_cli/parse_to_syncano/migrations/relation.py b/syncano_cli/parse_to_syncano/migrations/relation.py index 8368a02..53704c1 100644 --- a/syncano_cli/parse_to_syncano/migrations/relation.py +++ b/syncano_cli/parse_to_syncano/migrations/relation.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import six + from syncano.models import Object from syncano_cli.parse_to_syncano.config import PARSE_PAGINATION_LIMIT from syncano_cli.parse_to_syncano.migrations.aggregation import data_aggregate @@ -16,7 +18,7 @@ def __init__(self, class_name, relations, config): def process_class(self, instance): for relation in self.relations: - for field_name, relation_meta in relation.iteritems(): + for field_name, relation_meta in six.iteritems(relation): target_name = relation_meta['targetClass'] self._find_and_update_relations_objects( field_name=field_name, @@ -26,9 +28,9 @@ def process_class(self, instance): def _find_and_update_relations_objects(self, field_name, target_name, instance): # get the parse classes now; - for parse_class_name, objects_id_map in self.reference_map.iteritems(): + for parse_class_name, objects_id_map in six.iteritems(self.reference_map): if self.class_name == ClassProcessor.normalize_class_name(parse_class_name): - for parse_id, syncano_id in objects_id_map.iteritems(): + for parse_id, syncano_id in six.iteritems(objects_id_map): self._find_relations_for_object( parse_class_name=parse_class_name, target_name=target_name, diff --git a/syncano_cli/parse_to_syncano/processors/klass.py b/syncano_cli/parse_to_syncano/processors/klass.py index f9d2a03..f4fb7ab 100644 --- a/syncano_cli/parse_to_syncano/processors/klass.py +++ b/syncano_cli/parse_to_syncano/processors/klass.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import json +import six import click import requests @@ -65,7 +66,7 @@ def process_object(cls, parse_object, reference_map): syncano_fields = ClassProcessor.get_fields(parse_object.keys()) processed_object = {} files = {} - for key, value in parse_object.iteritems(): + for key, value in six.iteritems(parse_object): if isinstance(value, dict): if '__type' in value: if value['__type'] == ParseFieldTypeE.RELATION: @@ -126,7 +127,7 @@ def create_schema(cls, parse_schema): class_name = cls.normalize_class_name(parse_schema['className']) schema = [] relations = [] - for field, field_meta in parse_schema['fields'].iteritems(): + for field, field_meta in six.iteritems(parse_schema['fields']): if field not in fields_to_skip: type = field_meta['type'] new_type = ClassProcessor.map[type] diff --git a/syncano_cli/parse_to_syncano/processors/push_notifications.py b/syncano_cli/parse_to_syncano/processors/push_notifications.py index 2087251..4cf34a7 100644 --- a/syncano_cli/parse_to_syncano/processors/push_notifications.py +++ b/syncano_cli/parse_to_syncano/processors/push_notifications.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import six + from syncano.exceptions import SyncanoException from syncano.models import Class @@ -21,7 +23,7 @@ def process(self, parse_installation): @classmethod def find_user(cls, parse_installation): - for field_name, field_value in parse_installation.iteritems(): + for field_name, field_value in six.iteritems(parse_installation): if isinstance(field_value, dict) and field_value[u'className'] == u'_User': return True, field_value['objectId'] return False, None diff --git a/syncano_cli/sync/classes.py b/syncano_cli/sync/classes.py index 7291176..45bcb58 100644 --- a/syncano_cli/sync/classes.py +++ b/syncano_cli/sync/classes.py @@ -2,6 +2,7 @@ from __future__ import print_function, unicode_literals import re +import six import click @@ -100,7 +101,7 @@ def pull_classes(instance, include, update_dict=None): def push_classes(instance, class_dict): click.echo('INFO: Pushing classes.') - for name, config in class_dict.iteritems(): + for name, config in six.iteritems(class_dict): click.echo('INFO: Pushing class {0}'.format(name)) try: klass = instance.classes.get(name=name) @@ -109,7 +110,7 @@ def push_classes(instance, class_dict): klass = instance.classes.model(name=name) click.echo('INFO: Class {0} not found. Creating new one'.format(name)) schema = [] - for name, field_config in config['fields'].iteritems(): + for name, field_config in six.iteritems(config['fields']): field = {'name': name} field.update(schema_str_to_field(field_config)) schema.append(field) @@ -128,7 +129,7 @@ def validate_class(class_dict): if not isinstance(fields, dict): raise ValueError('Fields should be a dictionary.') - for name, spec in fields.iteritems(): + for name, spec in six.iteritems(fields): try: schema_str_to_field(spec) except ValueError as e: @@ -142,7 +143,7 @@ def validate_class(class_dict): def validate_classes(classes_dict): - for name, spec in classes_dict.iteritems(): + for name, spec in six.iteritems(classes_dict): validate_class(spec) for field, field_spec in spec['fields'].items(): diff --git a/syncano_cli/sync/scripts.py b/syncano_cli/sync/scripts.py index a112fd8..030aa54 100644 --- a/syncano_cli/sync/scripts.py +++ b/syncano_cli/sync/scripts.py @@ -192,7 +192,7 @@ def validate_script(script): 'on file extension'.format(**script) ) - for k, v in ALLOWED_RUNTIMES.iteritems(): + for k, v in six.iteritems(ALLOWED_RUNTIMES): if v == ext: script['runtime'] = runtime = k click.echo( diff --git a/tests/test_account.py b/tests/test_account.py index 71d99c3..e77d659 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -16,6 +16,9 @@ def test_register(self): os.remove(ACCOUNT_CONFIG_PATH) self.assertFalse(os.path.isfile(ACCOUNT_CONFIG_PATH)) + # old api key; + old_key = self.connection.connection().api_key + # make register; email = 'syncano.bot+977999{}@syncano.com'.format(random.randint(100000, 50000000)) self.runner.invoke(cli, args=['accounts', 'register', email], input='test1234\ntest1234', obj={}) @@ -23,10 +26,5 @@ def test_register(self): # check if api key is written in the config file; self.assert_config_variable_exists(ACCOUNT_CONFIG, 'DEFAULT', 'api_key') - # restore old connection in Syncano LIB; - self.connection = syncano.connect( - host=self.API_ROOT, - email=self.API_EMAIL, - password=self.API_PASSWORD, - api_key=self.API_KEY - ) + # restore old connection in Syncano LIB; To remove instance in tearDown; + self.connection.connection().api_key = old_key From e87bf99c2b32d6824bab0169d1c29c0936fcd8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 12:35:36 +0200 Subject: [PATCH 31/60] [CORE-1672] change all iteritems to six.iteritems; --- syncano_cli/parse_to_syncano/migrations/relation.py | 1 - syncano_cli/parse_to_syncano/processors/klass.py | 2 +- syncano_cli/parse_to_syncano/processors/push_notifications.py | 1 - syncano_cli/sync/classes.py | 2 +- syncano_cli/sync/scripts.py | 1 + tests/test_account.py | 1 - 6 files changed, 3 insertions(+), 5 deletions(-) diff --git a/syncano_cli/parse_to_syncano/migrations/relation.py b/syncano_cli/parse_to_syncano/migrations/relation.py index 53704c1..55c4b8f 100644 --- a/syncano_cli/parse_to_syncano/migrations/relation.py +++ b/syncano_cli/parse_to_syncano/migrations/relation.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import six - from syncano.models import Object from syncano_cli.parse_to_syncano.config import PARSE_PAGINATION_LIMIT from syncano_cli.parse_to_syncano.migrations.aggregation import data_aggregate diff --git a/syncano_cli/parse_to_syncano/processors/klass.py b/syncano_cli/parse_to_syncano/processors/klass.py index f4fb7ab..d2acf28 100644 --- a/syncano_cli/parse_to_syncano/processors/klass.py +++ b/syncano_cli/parse_to_syncano/processors/klass.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- import json -import six import click import requests +import six from syncano_cli.parse_to_syncano.migrations.aggregation import ClassAggregate from syncano_cli.parse_to_syncano.parse.constants import ParseFieldTypeE diff --git a/syncano_cli/parse_to_syncano/processors/push_notifications.py b/syncano_cli/parse_to_syncano/processors/push_notifications.py index 4cf34a7..0857d8d 100644 --- a/syncano_cli/parse_to_syncano/processors/push_notifications.py +++ b/syncano_cli/parse_to_syncano/processors/push_notifications.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import six - from syncano.exceptions import SyncanoException from syncano.models import Class diff --git a/syncano_cli/sync/classes.py b/syncano_cli/sync/classes.py index 45bcb58..f8db7e9 100644 --- a/syncano_cli/sync/classes.py +++ b/syncano_cli/sync/classes.py @@ -2,9 +2,9 @@ from __future__ import print_function, unicode_literals import re -import six import click +import six ALLOWED_TYPES = {"array", "boolean", "datetime", "file", "float", "geopoint", "integer", "object", "reference", "relation", "string", diff --git a/syncano_cli/sync/scripts.py b/syncano_cli/sync/scripts.py index 030aa54..dcddf92 100644 --- a/syncano_cli/sync/scripts.py +++ b/syncano_cli/sync/scripts.py @@ -6,6 +6,7 @@ from collections import defaultdict import click +import six from syncano.exceptions import SyncanoRequestError ALLOWED_RUNTIMES = { diff --git a/tests/test_account.py b/tests/test_account.py index e77d659..d01e0fc 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -2,7 +2,6 @@ import os import random -import syncano from syncano_cli.config import ACCOUNT_CONFIG, ACCOUNT_CONFIG_PATH from syncano_cli.main import cli from tests.base import BaseCLITest From 9ae973244c577168590b4057241c0212673ef609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 12:50:23 +0200 Subject: [PATCH 32/60] [CORE-1672] corrects tests; --- tests/test_migrate_commands.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index 94cc74c..f5a91cc 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -16,6 +16,7 @@ def setUpClass(cls): # do a login first; cls.runner.invoke(cli, args=['login', '--instance-name', cls.instance.name], obj={}) # and make setup; + cls.old_key = cls.connection.connection().api_key cls._set_up_configuration() @classmethod @@ -104,3 +105,7 @@ def test_object_migrations(self, request_mock): channels_class = self.instance.classes.get(name=DEVICE_CHANNELS_CLASS_NAME) self.assertEqual(len([channel_cl for channel_cl in channels_class.objects.all()]), 2) + + def tearDownClass(cls): + cls.connection.connection().api_key = cls.old_key + super(MigrateCommandsTest, cls).tearDownClass() From 07e8322fa9aadce2f0d36969a7386c4168cff10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 14:02:36 +0200 Subject: [PATCH 33/60] [CORE-1672] correct file management in hosting, correct parse to syncano file handling; --- syncano_cli/hosting/command.py | 9 +++++---- syncano_cli/parse_to_syncano/commands.py | 2 +- syncano_cli/parse_to_syncano/processors/klass.py | 4 ++-- syncano_cli/sync/project.py | 2 +- syncano_cli/sync/scripts.py | 2 +- tests/base.py | 2 +- tests/test_migrate_commands.py | 1 + 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/syncano_cli/hosting/command.py b/syncano_cli/hosting/command.py index 91a335b..e81aff5 100644 --- a/syncano_cli/hosting/command.py +++ b/syncano_cli/hosting/command.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import os +import re import click from syncano_cli.base.command import BaseInstanceCommand @@ -8,6 +9,8 @@ class HostingCommands(BaseInstanceCommand): + VALID_PATH_REGEX = re.compile(r'^(?!/)([a-zA-Z0-9\-\._]+/{0,1})+(? Date: Mon, 19 Sep 2016 14:28:17 +0200 Subject: [PATCH 34/60] [CORE-1672] correct tests asserts; --- tests/test_execute_commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_execute_commands.py b/tests/test_execute_commands.py index c409c0d..22099af 100644 --- a/tests/test_execute_commands.py +++ b/tests/test_execute_commands.py @@ -57,7 +57,7 @@ def test_script_endpoint_with_payload(self): '-d', 'data=some_nice_string' ], obj={}) - self.assertEqual(result.output, 'some_nice_string\n') + self.assertIn('some_nice_string\n', result.output) def test_script_endpoint_custom_response(self): name_prefix = 'custom_response' @@ -78,4 +78,4 @@ def test_script_endpoint_custom_response(self): 'execute', '{}_test_script_endpoint'.format(name_prefix) ], obj={}) - self.assertEqual(result.output, """{\n "test": "amazing thing"\n}\n""") + self.assertIn("""{\n "test": "amazing thing"\n}\n""", result.output) From 18bafae76b0cbf3d03dbbad13d5fa491a1ee0463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 14:53:59 +0200 Subject: [PATCH 35/60] [CORE-1672] change file transfer - to do not change the LIB; --- syncano_cli/parse_to_syncano/migrations/transfer.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/syncano_cli/parse_to_syncano/migrations/transfer.py b/syncano_cli/parse_to_syncano/migrations/transfer.py index 2fe6d60..5f19f85 100644 --- a/syncano_cli/parse_to_syncano/migrations/transfer.py +++ b/syncano_cli/parse_to_syncano/migrations/transfer.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import six + import click from syncano.exceptions import SyncanoException from syncano.models import APNSDevice, GCMDevice, Object @@ -158,12 +160,15 @@ def transfer_files(self): show_pos=True) as parse_ids: for parse_id in parse_ids: files, syncano_class_name, parse_class_name = self.file_descriptors[parse_id] - Object.please.update( + object_to_update = Object( class_name=syncano_class_name, id=self.data.reference_map[parse_class_name][parse_id], - files=files ) + for file_name, object_file in six.iteritems(files): + setattr(object_to_update, file_name, object_file) + object_to_update.save() + def get_class(self, instance, class_name): s_class = self.syncano_classes.get(class_name) if not s_class: From 8cd98295e73ce52427df760864c18e62be1432b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 15:00:44 +0200 Subject: [PATCH 36/60] [CORE-1672] correct isort issues; --- syncano_cli/parse_to_syncano/migrations/transfer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/syncano_cli/parse_to_syncano/migrations/transfer.py b/syncano_cli/parse_to_syncano/migrations/transfer.py index 5f19f85..75df269 100644 --- a/syncano_cli/parse_to_syncano/migrations/transfer.py +++ b/syncano_cli/parse_to_syncano/migrations/transfer.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -import six - import click +import six from syncano.exceptions import SyncanoException from syncano.models import APNSDevice, GCMDevice, Object from syncano_cli.parse_to_syncano.config import PARSE_PAGINATION_LIMIT, SYNCANO_BATCH_SIZE From ea682d9d01bdf6f6c446de538d0f89638bca403e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 15:49:29 +0200 Subject: [PATCH 37/60] [CORE-1672] correct configure test; --- syncano_cli/main.py | 7 +++++-- tests/test_migrate_commands.py | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/syncano_cli/main.py b/syncano_cli/main.py index 3214ad9..af4d6e3 100644 --- a/syncano_cli/main.py +++ b/syncano_cli/main.py @@ -2,6 +2,7 @@ from __future__ import print_function import click +from click import ClickException from syncano.exceptions import SyncanoException from syncano_cli.account.commands import top_account from syncano_cli.base.exceptions import CLIBaseException, SyncanoLibraryException @@ -33,5 +34,7 @@ def main(): cli(obj={}) except SyncanoException as e: raise SyncanoLibraryException(e.reason) - except Exception as e: - raise CLIBaseException(e) + except ClickException: + raise CLIBaseException('CLI exception.') + else: + raise diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index c5b1e10..e3f437f 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -33,8 +33,10 @@ def test_configure(self): result = self.runner.invoke(cli, args=['migrate', 'configure'], obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) - result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], input='xxx\nxxx\nxxx\nxxx\n', - obj={}) + result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], input='xxx\nxxx\n{}\n{}\n'.format( + self.instance.name, + ACCOUNT_CONFIG.get("DEFAULT", "key") + ), obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) result = self.runner.invoke(cli, args=['migrate', 'configure', '--current'], obj={}) From 01ecea271085799b3e6eb9f6288ed95fddaf5fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 19 Sep 2016 15:59:51 +0200 Subject: [PATCH 38/60] [CORE-1672] correct attributes ordering; --- tests/test_migrate_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_migrate_commands.py b/tests/test_migrate_commands.py index e3f437f..980dbb3 100644 --- a/tests/test_migrate_commands.py +++ b/tests/test_migrate_commands.py @@ -34,8 +34,8 @@ def test_configure(self): self.assertIn('PARSE_MASTER_KEY', result.output) result = self.runner.invoke(cli, args=['migrate', 'configure', '--force'], input='xxx\nxxx\n{}\n{}\n'.format( + ACCOUNT_CONFIG.get("DEFAULT", "key"), self.instance.name, - ACCOUNT_CONFIG.get("DEFAULT", "key") ), obj={}) self.assertIn('PARSE_MASTER_KEY', result.output) From cb23d809e9dacfd820be183501cf270709f84e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Tue, 20 Sep 2016 15:23:06 +0200 Subject: [PATCH 39/60] [CORE-1682] remove custom formatting for examples; --- syncano_cli/custom_sockets/formatters.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index 298505d..0a62932 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import json import os from collections import defaultdict @@ -74,19 +73,6 @@ def _json_process_endpoints(cls, endpoints): return api_endpoints - @classmethod - def _process_response_example(cls, metadata_key, inner_data): - if metadata_key not in cls.ENDPOINT_TYPES: - if metadata_key == 'response': - if 'example' in inner_data: - if isinstance(inner_data['example'], dict): - inner_data['example'] = inner_data['example'] - elif isinstance(inner_data['example'], six.string_types): - try: - inner_data['example'] = json.loads(inner_data['example']) - except (TypeError, ValueError): - inner_data['example'] = inner_data['example'] - @classmethod def _get_metadata(cls, endpoint_data): metadata = defaultdict(dict) @@ -98,14 +84,12 @@ def _get_metadata(cls, endpoint_data): if metadata_key == 'parameters': metadata[metadata_key][data_key] = inner_data else: - cls._process_response_example(metadata_key, inner_data) metadata[metadata_key] = inner_data elif data_key not in cls.ENDPOINT_TYPES: if data_key == 'parameters': metadata[data_key]['*'] = data else: - cls._process_response_example(data_key, data) metadata[data_key] = data metadata[data_key] = data From 47e03b57dd8cb27b24533624484dfdbfd631bfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Wed, 21 Sep 2016 16:39:17 +0200 Subject: [PATCH 40/60] [CORE-1684] add support for multiple domains; --- README.rst | 18 ++++++-- syncano_cli/hosting/command.py | 27 ++++++++--- syncano_cli/hosting/commands.py | 45 +++++++++++------- syncano_cli/hosting/exceptions.py | 4 +- tests/test_hosting_commands.py | 76 ++++++++++++++++--------------- 5 files changed, 104 insertions(+), 66 deletions(-) diff --git a/README.rst b/README.rst index 9961533..58068e7 100644 --- a/README.rst +++ b/README.rst @@ -263,19 +263,19 @@ Syncano Hosting Syncano Hosting is a simple way to host your static files on Syncano servers. The CLI supports it in the following way: -This command will list files for currently hosted website:: +This command will list files for currently defined hostings in the instance:: syncano hosting list +This command will list files for currently hosted website (in `default` hosting):: + + syncano hosting list files + This command will publish all files inside ** to the default Syncano Hosting instance. When publishing the whole directory, the structure will be mapped on Syncano.:: syncano hosting publish -This command will unpublish currently published hosting:: - - syncano hosting unpublish - This command will permamently delete the hosting:: @@ -289,6 +289,14 @@ This command will update single file:: syncano hosting update hosting/file/path local/file/path +For each of the above command you can specify the domain to change just after hosting command, example:: + + syncano hosting --domain staging publish + +Will create a new hosting which will be available under: `--staging.syncano.site` +If this hosting is also a default one, it will be available under: `.syncano.site`. + + Custom Sockets ============== diff --git a/syncano_cli/hosting/command.py b/syncano_cli/hosting/command.py index e81aff5..c15cbdc 100644 --- a/syncano_cli/hosting/command.py +++ b/syncano_cli/hosting/command.py @@ -4,18 +4,31 @@ import click from syncano_cli.base.command import BaseInstanceCommand -from syncano_cli.hosting.exceptions import NoDefaultHostingFoundException, PathNotFoundException, UnicodeInPathException +from syncano_cli.hosting.exceptions import NoHostingFoundException, PathNotFoundException, UnicodeInPathException class HostingCommands(BaseInstanceCommand): VALID_PATH_REGEX = re.compile(r'^(?!/)([a-zA-Z0-9\-\._]+/{0,1})+(? Date: Wed, 21 Sep 2016 16:44:29 +0200 Subject: [PATCH 41/60] [CORE-1684] correct README; --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 58068e7..8a780f7 100644 --- a/README.rst +++ b/README.rst @@ -263,11 +263,11 @@ Syncano Hosting Syncano Hosting is a simple way to host your static files on Syncano servers. The CLI supports it in the following way: -This command will list files for currently defined hostings in the instance:: +This command will list currently defined hostings in the instance:: syncano hosting list -This command will list files for currently hosted website (in `default` hosting):: +This command will list files for currently hosted website (for `default` hosting):: syncano hosting list files From 88dc5ad3f66f672d54f47cd19229838b860c1cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Wed, 21 Sep 2016 19:33:36 +0200 Subject: [PATCH 42/60] [CORE-1684] correct domain option in tests; --- tests/test_hosting_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_hosting_commands.py b/tests/test_hosting_commands.py index e95fd0b..b80ce75 100644 --- a/tests/test_hosting_commands.py +++ b/tests/test_hosting_commands.py @@ -69,5 +69,5 @@ def _delete_hosting(self, domain=None): @classmethod def _extend_args(cls, args, domain): if domain: - args.insert(1, '--domains') + args.insert(1, '--domain') args.insert(2, domain) From fc6efbaddc9d62b22f0dc082b7788d7261e21dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Wed, 21 Sep 2016 19:55:45 +0200 Subject: [PATCH 43/60] [CORE-1684] add domain to delete file in tests; --- tests/test_hosting_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_hosting_commands.py b/tests/test_hosting_commands.py index b80ce75..7c984c5 100644 --- a/tests/test_hosting_commands.py +++ b/tests/test_hosting_commands.py @@ -13,7 +13,7 @@ def _base_test(self, domain): self.assertIn('css/page.css', result.output) # test single file deletion; - self._delete_single_file('css/page.css') + self._delete_single_file('css/page.css', domain=domain) result = self._get_list_files_output(domain=domain) self.assertNotIn('css/page.css', result.output) From 3afb6e54f9c4cdfdbe9795a2a24f2138f63795de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Wed, 21 Sep 2016 20:49:13 +0200 Subject: [PATCH 44/60] [CORE-1684] correct test assert; --- tests/test_hosting_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_hosting_commands.py b/tests/test_hosting_commands.py index 7c984c5..0177093 100644 --- a/tests/test_hosting_commands.py +++ b/tests/test_hosting_commands.py @@ -26,7 +26,7 @@ def _base_test(self, domain): # test hosting delete; self._delete_hosting(domain=domain) result = self._get_list_files_output(domain=domain) - self.assertIn('Hosting with domain `default` - not found. Exit.', result.output) + self.assertIn('Hosting with domain `{}` - not found. Exit.'.format(domain or 'default'), result.output) # recreate hosting; self._publish_files(domain=domain) From 335163861663abdeb410b09a9a0ab05c0baf4bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Wed, 21 Sep 2016 21:46:16 +0200 Subject: [PATCH 45/60] [CORE-1684] correct test assert; --- tests/test_execute_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_execute_commands.py b/tests/test_execute_commands.py index 22099af..9a0a8c6 100644 --- a/tests/test_execute_commands.py +++ b/tests/test_execute_commands.py @@ -42,7 +42,7 @@ def test_script_endpoint(self): 'execute', '{}_test_script_endpoint'.format(name_prefix) ], obj={}) - self.assertEqual(result.output, '12\n') + self.assertIn('12\n', result.output) def test_script_endpoint_with_payload(self): name_prefix = 'payload_response' From ae0675686e438213fe92aee034fdd115aecfe0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 23 Sep 2016 16:04:06 +0200 Subject: [PATCH 46/60] [CORE-1685] add class dependency support, correct config handling; --- README.rst | 2 +- syncano_cli/custom_sockets/command.py | 14 +++---- syncano_cli/custom_sockets/exceptions.py | 4 +- syncano_cli/custom_sockets/formatters.py | 50 ++++++++++++++++++++---- syncano_cli/custom_sockets/parsers.py | 38 +++++++++++------- 5 files changed, 74 insertions(+), 34 deletions(-) diff --git a/README.rst b/README.rst index 8a780f7..52eef0d 100644 --- a/README.rst +++ b/README.rst @@ -335,7 +335,7 @@ Create a template from an existing Custom Socket:: syncano sockets template /path/to/out --socket socket_name -Run endpoint defined in Custom Socket::s +Run endpoint defined in Custom Socket:: syncano sockets run socket_name/endpoint_name diff --git a/syncano_cli/custom_sockets/command.py b/syncano_cli/custom_sockets/command.py index 68d3258..a798099 100644 --- a/syncano_cli/custom_sockets/command.py +++ b/syncano_cli/custom_sockets/command.py @@ -45,18 +45,18 @@ def install_from_dir(self, dir_path): with open(os.path.join(dir_path, self.SOCKET_FILE_NAME)) as socket_file: yml_file = yaml.safe_load(socket_file) - self.set_up_config(yml_file) + config = self.set_up_config(yml_file) api_data = SocketFormatter.to_json(socket_yml=yml_file, directory=dir_path) api_data.update({'instance_name': self.instance.name}) + api_data.update({'config': config}) custom_socket = CustomSocket.please.create(**api_data) click.echo('INFO: socket {} created.'.format(custom_socket.name)) def install_from_url(self, url_path, name): socket_yml = self.fetch_file(url_path) - self.set_up_config(socket_yml) - - CustomSocket(name=name).install_from_url(url=url_path, instance_name=self.instance.name) + config = self.set_up_config(socket_yml) + CustomSocket(name=name).install_from_url(url=url_path, instance_name=self.instance.name, config=config) click.echo('INFO: Installing socket from url: do `syncano sockets list` to obtain the status.') def run(self, endpoint_name, method='GET', data=None): @@ -108,13 +108,9 @@ def create_template_from_local_template(self, destination): script_file.write(script_source) def set_up_config(self, socket_yml): - instance_config = self.instance.get_config() - socket_config = SocketConfigParser(socket_yml=socket_yml) if socket_config.is_valid(): - provided_config = socket_config.ask_for_config(instance_config) - instance_config.update(provided_config) - self.instance.set_config(instance_config) + return socket_config.ask_for_config() def fetch_file(self, url_path): response = requests.get(url_path) diff --git a/syncano_cli/custom_sockets/exceptions.py b/syncano_cli/custom_sockets/exceptions.py index ad2447c..f21a66e 100644 --- a/syncano_cli/custom_sockets/exceptions.py +++ b/syncano_cli/custom_sockets/exceptions.py @@ -19,8 +19,8 @@ class SocketFileFetchException(CLIBaseException): default_message = u'Can not fetch the file: {}.' -class ConfigNameMissingException(CLIBaseException): - default_message = u'Variable name should be provided in config.' +class BadConfigFormatException(CLIBaseException): + default_message = u'Bad config format.' class BadYAMLDefinitionInEndpointsException(CLIBaseException): diff --git a/syncano_cli/custom_sockets/formatters.py b/syncano_cli/custom_sockets/formatters.py index 0a62932..182eb7c 100644 --- a/syncano_cli/custom_sockets/formatters.py +++ b/syncano_cli/custom_sockets/formatters.py @@ -8,12 +8,24 @@ from syncano_cli.sync.scripts import ALLOWED_RUNTIMES +class DependencyTypeE(): + CLASS = 'class' + SCRIPT = 'script' + + class SocketFormatter(object): SOCKET_FIELDS = ['name', 'description', 'endpoints', 'dependencies'] HTTP_METHODS = ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'] ENDPOINT_TYPES = ['script'] - DEPENDENCY_TYPES = {'scripts': 'script'} + DEPENDENCY_TYPES = {'scripts': DependencyTypeE.SCRIPT, 'classes': DependencyTypeE.CLASS} + + @classmethod + def get_dependency_handlers(cls): + handlers = {} + for yml_name, dependency_type in six.iteritems(cls.DEPENDENCY_TYPES): + handlers[dependency_type] = getattr(cls, 'get_{}_dependency'.format(dependency_type)) + return handlers @classmethod def to_yml(cls, socket_object): @@ -126,18 +138,42 @@ def _get_calls(cls, endpoint_data): @classmethod def _json_process_dependencies(cls, dependencies, directory): api_dependencies = [] - + dependency_handlers = cls.get_dependency_handlers() for dependencies_type, dependency in six.iteritems(dependencies): if dependencies_type in cls.DEPENDENCY_TYPES: for dependency_name, data in six.iteritems(dependency): - api_dependencies.append({ - 'type': cls.DEPENDENCY_TYPES[dependencies_type], - 'runtime_name': data['runtime_name'], + dependency_type = cls.DEPENDENCY_TYPES[dependencies_type] + base_dependency_data = { 'name': dependency_name, - 'source': cls._get_source(data['file'], directory), - }) + 'type': dependency_type + } + typed_dependency_data = dependency_handlers[dependency_type](data, directory=directory) + typed_dependency_data.update(base_dependency_data) + api_dependencies.append(typed_dependency_data) return api_dependencies + @classmethod + def get_script_dependency(cls, data, **kwargs): + """ + Note: when definig new depenency processors use following pattern: + get_{name}_dependency -> where {name} is one of the defined in DepedencyTypeE + this allows to easily extend dependency handling; + :param data: + :param directory: + :return: + """ + directory = kwargs.get('directory') + return { + 'runtime_name': data['runtime_name'], + 'source': cls._get_source(data['file'], directory), + } + + @classmethod + def get_class_dependency(cls, data, **kwargs): + return { + 'schema': data['schema'] + } + @classmethod def _get_source(cls, file_name, directory): with open(os.path.join(directory, '{}'.format(file_name)), 'r+') as source_file: diff --git a/syncano_cli/custom_sockets/parsers.py b/syncano_cli/custom_sockets/parsers.py index b53bd59..5389861 100644 --- a/syncano_cli/custom_sockets/parsers.py +++ b/syncano_cli/custom_sockets/parsers.py @@ -1,34 +1,42 @@ # -*- coding: utf-8 -*- - import click -from syncano_cli.custom_sockets.exceptions import ConfigNameMissingException +import six +from syncano_cli.custom_sockets.exceptions import BadConfigFormatException class SocketConfigParser(object): + PROMPT_FIELD = 'prompt' + CONSTANTS_FIELD = 'constants' + def __init__(self, socket_yml): self.config = socket_yml.get('config', []) def is_valid(self): - for config_var in self.config: - if not config_var.get('name'): - raise ConfigNameMissingException() + valid = True + for field_name in [self.PROMPT_FIELD, self.CONSTANTS_FIELD]: + valid &= self._is_valid(field_name) + if not valid: + raise BadConfigFormatException() + return valid + + def _is_valid(self, field_name): + if field_name in self.config and not len(self.config[field_name]): + return False return True - def ask_for_config(self, instance_config): + def ask_for_config(self): provided_config = {} - for config_var in self.config: - config_var_name = config_var['name'] - - if config_var_name not in instance_config: - config_var_value = click.prompt(self.get_prompt_str(config_var)) + if self.PROMPT_FIELD in self.config: + for config_var_name, config_metadata in six.iteritems(self.config[self.PROMPT_FIELD]): + config_var_value = click.prompt(self.get_prompt_str(config_var_name, config_metadata)) provided_config[config_var_name] = config_var_value return provided_config @staticmethod - def get_prompt_str(config_var): - prompt_str = 'Provide value for {}'.format(config_var['name']) - if config_var.get('description', None): - prompt_str = '{} ({})'.format(prompt_str, config_var['description']) + def get_prompt_str(field_name, config_metadata): + prompt_str = 'Provide value for {}'.format(field_name) + if config_metadata.get('description'): + prompt_str = '{} ({})'.format(prompt_str, config_metadata['description']) return prompt_str From 8f6a6b1b5d08bc884c7c1dd21a429f66b84453a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 23 Sep 2016 16:11:38 +0200 Subject: [PATCH 47/60] [CORE-1685] update yml file documentation; --- docs/custom_sockets/docs.md | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/docs/custom_sockets/docs.md b/docs/custom_sockets/docs.md index f0996fc..e5395fc 100644 --- a/docs/custom_sockets/docs.md +++ b/docs/custom_sockets/docs.md @@ -7,6 +7,13 @@ author: name: Sebastian email: sebastian@syncano.com + config: + constants: + secret_key: value + prompt: + user_key: + type: string + description: A Syncano user key icon: name: icon_name color: red @@ -34,6 +41,20 @@ runtime_name: python_library_v5.0 file: scripts/script3.py + classes: + country: + schema: + - name: name + type: string + - name: topLevelDomain + type: string + - name: capital + type: string + - name: alpha2Code + type: string + - name: alpha3Code + type: string + ### YAML file structure explanation * `name` is the name of your new Custom Socket - this should be unique; @@ -46,6 +67,8 @@ * can be found in `metadata` field on Custom Socket in Syncano Dasboard. * `icon` is metadata information about your Custom Socket - it stores the icon name used and its color (used in Syncano Dashboard) * `endpoints` - definition of the endpoints in a Custom Socket; Currently supported endpoints can be only of `script` type. +* `config` - stores the metadata about custom socket configuration; constants are config variables that are passed one-to-one + from yaml file definitions; the `prompt` config section - this variables will be requested from user during installation. Consider this example: @@ -76,8 +99,8 @@ The difference is that we now define what happens for the different HTTP methods. When the GET HTTP method is used, `script_endpoint_3` script endpoint will be run. When the POST HTTP method is used - `script_endpoint_2` endpoint will be executed. - Currently only Script Endpoints are supported, which run scripts under the hood. But don't worry, - we are working on adding more options! + Currently Script Endpoints and Classes are supported, which run scripts under the hood. + We are working on adding more options! * `dependencies` - the definition of your Custom Socket dependencies. They define all dependency objects which will be called when the endpoint is requested. @@ -90,7 +113,7 @@ which will be called when the endpoint is requested. runtime_name: python_library_v5.0 file: scripts/script1.py - Above YAML snippet defines one dependency: + Above YAML snippet defines four dependencies (three of type `script` and one of type `class`): * `script` - type of the dependency (defined using `scripts` keyword). * `script_endpoint_1` - name of the dependency; it's an important element, because that's the place where you connect a dependency to an endpoint. * `runtime_name` is name of the runtime used in a script; @@ -99,6 +122,26 @@ which will be called when the endpoint is requested. It should be noted that when defining Custom Scripts, we suggest following some basic directory structure- for better work organization. We recommend storing scripts under the `scripts` directory - this is why the filename is a relative path: `scripts/script1.py`. Of course your can also follow your own rules, e.g. by using a flat file structure. + + The class dependency looks as follows: + + classes: + country: + schema: + - name: name + type: string + - name: topLevelDomain + type: string + - name: capital + type: string + - name: alpha2Code + type: string + - name: alpha3Code + type: string + + This simple mean that Custom Socket requires a class `country` to work properly. Under the hood - Syncano Platform + will check if this class exists (if not - create it) and ensure that all required files defined in `schema` are present. + ## Custom Socket directory structure From e2da2cb02ab5ef220d5b980571149972cf5c1b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 23 Sep 2016 16:23:47 +0200 Subject: [PATCH 48/60] [CORE-1685] add class dependecy to the socket template definition; --- .../custom_sockets/templates/socket_template.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/syncano_cli/custom_sockets/templates/socket_template.py b/syncano_cli/custom_sockets/templates/socket_template.py index a5ddecb..4b1eae0 100644 --- a/syncano_cli/custom_sockets/templates/socket_template.py +++ b/syncano_cli/custom_sockets/templates/socket_template.py @@ -32,6 +32,20 @@ custom_script_2: runtime_name: python_library_v5.0 file: scripts/custom_script_2.py + + classes: + country: + schema: + - name: name + type: string + - name: topLevelDomain + type: string + - name: capital + type: string + - name: alpha2Code + type: string + - name: alpha3Code + type: string """ SCRIPTS = { From 8f89cd326a9b759152efa415d9471bd6037a2c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Fri, 23 Sep 2016 16:36:58 +0200 Subject: [PATCH 49/60] [CORE-1685] BUMP python lib version; --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e42234a..e4e32aa 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ url='https://github.com/Syncano/syncano-cli', packages=find_packages(), license='MIT', - install_requires=['syncano>=5.4.2', 'PyYaml>=3.11', 'watchdog>=0.8.3', 'click>=6.6'], + install_requires=['syncano>=5.4.3', 'PyYaml>=3.11', 'watchdog>=0.8.3', 'click>=6.6'], test_suite='tests', entry_points=""" [console_scripts] From ce9b3cf8ecbdca862cabfea047d09a3719130a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 11:43:46 +0200 Subject: [PATCH 50/60] [CORE-1685] add possibility to read CustomSocket configuration; --- README.rst | 5 +++++ syncano_cli/custom_sockets/command.py | 6 ++++++ syncano_cli/custom_sockets/commands.py | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/README.rst b/README.rst index 52eef0d..59d4ab6 100644 --- a/README.rst +++ b/README.rst @@ -323,6 +323,10 @@ Display chosen Custom Socket details:: syncano sockets details socket_name +Display Custom Socket config (with name: `socket_name`):: + + syncano sockets config socket_name + Delete a Custom Socket:: syncano sockets delete socket_name @@ -343,6 +347,7 @@ Run endpoint providing POST data:: syncano sockets run socket_name/my_endpoint_12 POST -d one=1 + In all of the above cases you can override the Syncano instance being used:: --instance-name my_instance_name diff --git a/syncano_cli/custom_sockets/command.py b/syncano_cli/custom_sockets/command.py index a798099..bbce82b 100644 --- a/syncano_cli/custom_sockets/command.py +++ b/syncano_cli/custom_sockets/command.py @@ -31,6 +31,12 @@ def details(self, socket_name): cs = CustomSocket.please.get(name=socket_name, instance_name=self.instance.name) click.echo(SocketFormatter.format_socket_details(cs)) + def config(self, socket_name): + cs = CustomSocket.please.get(name=socket_name, instance_name=self.instance.name) + click.echo('Config for socket `{}`'.format(cs.name)) + for name, value in six.iteritems(cs.config): + click.echo('{:20}: {}'.format(name, value)) + def list_endpoints(self): endpoints = SocketEndpoint.get_all_endpoints(instance_name=self.instance.name) click.echo(SocketFormatter.format_endpoints_list(socket_endpoints=endpoints)) diff --git a/syncano_cli/custom_sockets/commands.py b/syncano_cli/custom_sockets/commands.py index 11bb864..3b98144 100644 --- a/syncano_cli/custom_sockets/commands.py +++ b/syncano_cli/custom_sockets/commands.py @@ -63,6 +63,14 @@ def details(ctx, socket_name): socket_command.details(socket_name=socket_name) +@sockets.command() +@click.pass_context +@click.argument('socket_name') +def config(ctx, socket_name): + socket_command = ctx.obj['socket_command'] + socket_command.config(socket_name=socket_name) + + @sockets.command() @click.pass_context @click.argument('socket_name') From ab3c65a5cd0b9cb703bd2e7505c01a835b4a2eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 11:46:07 +0200 Subject: [PATCH 51/60] [CORE-1685] correct test with custom socket config; --- tests/test_custom_sockets_commands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_custom_sockets_commands.py b/tests/test_custom_sockets_commands.py index eebd4e7..11d9784 100644 --- a/tests/test_custom_sockets_commands.py +++ b/tests/test_custom_sockets_commands.py @@ -43,13 +43,13 @@ def test_dir_install(self): self.assertIn('custom_socket_example', result.output) # check config; - result = self.runner.invoke(cli, args=['config'], obj={}) + result = self.runner.invoke(cli, args=['sockets', 'config', '{}'.format('custom_socket_example')], obj={}) self.assertIn('xx-9876', result.output) def test_url_install(self): path = 'https://raw.githubusercontent.com/Syncano/custom-socket-test/master/socket.yml' - - self.runner.invoke(cli, args=['sockets', 'install', path, '--name', 'my_tweet1'], + socket_name = 'my_tweet1' + self.runner.invoke(cli, args=['sockets', 'install', path, '--name', socket_name], input='testyx\ntestx\n', obj={}) time.sleep(2) # wait for socket creation; result = self.runner.invoke(cli, args=['sockets', 'list'], obj={}) @@ -61,7 +61,7 @@ def test_url_install(self): self.assertNotIn('my_tweet1', result.output) # check config; - result = self.runner.invoke(cli, args=['config'], obj={}) + result = self.runner.invoke(cli, args=['sockets', 'config', '{}'.format(socket_name)], obj={}) self.assertIn('testyx', result.output) self.assertIn('testx', result.output) From f3f93b5450de1f5f72d2b20f0a568b0bea1e28d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 12:10:14 +0200 Subject: [PATCH 52/60] [CORE-1685] re-organize tests; correct template (use proper config now); --- .../custom_sockets/templates/socket_template.py | 8 ++++++-- tests/test_custom_sockets_commands.py | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/syncano_cli/custom_sockets/templates/socket_template.py b/syncano_cli/custom_sockets/templates/socket_template.py index 4b1eae0..35b6c2c 100644 --- a/syncano_cli/custom_sockets/templates/socket_template.py +++ b/syncano_cli/custom_sockets/templates/socket_template.py @@ -8,8 +8,12 @@ name: icon-name color: ffee11 config: - - name: custom_api_key - description: an Api Key for third party service + constants: + some_key: some_value + prompt: + custom_api_key: + type: sting + description: an Api Key for third party service description: Some custom integration endpoints: custom_endpoint: diff --git a/tests/test_custom_sockets_commands.py b/tests/test_custom_sockets_commands.py index 11d9784..85677fd 100644 --- a/tests/test_custom_sockets_commands.py +++ b/tests/test_custom_sockets_commands.py @@ -52,6 +52,12 @@ def test_url_install(self): self.runner.invoke(cli, args=['sockets', 'install', path, '--name', socket_name], input='testyx\ntestx\n', obj={}) time.sleep(2) # wait for socket creation; + + # check config; + result = self.runner.invoke(cli, args=['sockets', 'config', '{}'.format(socket_name)], obj={}) + self.assertIn('testyx', result.output) + self.assertIn('testx', result.output) + result = self.runner.invoke(cli, args=['sockets', 'list'], obj={}) self.assertIn('my_tweet1', result.output) self.assertNotIn('error', result.output) @@ -60,11 +66,6 @@ def test_url_install(self): result = self.runner.invoke(cli, args=['sockets', 'list'], obj={}) self.assertNotIn('my_tweet1', result.output) - # check config; - result = self.runner.invoke(cli, args=['sockets', 'config', '{}'.format(socket_name)], obj={}) - self.assertIn('testyx', result.output) - self.assertIn('testx', result.output) - def test_template_from_socket(self): socket_base_path = '/tmp/socket_from_socket' self.runner.invoke(cli, args=['sockets', 'template', socket_base_path, From 4b1fe010b112038dfd6399d78d0fd555fa41b887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 12:19:54 +0200 Subject: [PATCH 53/60] [CORE-1685] correct tests; --- tests/test_custom_sockets_commands.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_custom_sockets_commands.py b/tests/test_custom_sockets_commands.py index 85677fd..f985e40 100644 --- a/tests/test_custom_sockets_commands.py +++ b/tests/test_custom_sockets_commands.py @@ -55,7 +55,6 @@ def test_url_install(self): # check config; result = self.runner.invoke(cli, args=['sockets', 'config', '{}'.format(socket_name)], obj={}) - self.assertIn('testyx', result.output) self.assertIn('testx', result.output) result = self.runner.invoke(cli, args=['sockets', 'list'], obj={}) From 87cdf4582ca5e2c09d40913e1a200c1a18a065b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 12:57:51 +0200 Subject: [PATCH 54/60] [CORE-1685] correct tests; --- tests/test_custom_sockets_commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_custom_sockets_commands.py b/tests/test_custom_sockets_commands.py index f985e40..6ad80ce 100644 --- a/tests/test_custom_sockets_commands.py +++ b/tests/test_custom_sockets_commands.py @@ -50,12 +50,12 @@ def test_url_install(self): path = 'https://raw.githubusercontent.com/Syncano/custom-socket-test/master/socket.yml' socket_name = 'my_tweet1' self.runner.invoke(cli, args=['sockets', 'install', path, '--name', socket_name], - input='testyx\ntestx\n', obj={}) + input='testyx\n', obj={}) time.sleep(2) # wait for socket creation; # check config; result = self.runner.invoke(cli, args=['sockets', 'config', '{}'.format(socket_name)], obj={}) - self.assertIn('testx', result.output) + self.assertIn('testyx', result.output) result = self.runner.invoke(cli, args=['sockets', 'list'], obj={}) self.assertIn('my_tweet1', result.output) From 852769e98e0707253b3351f6f1bb68a0878623c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 16:08:11 +0200 Subject: [PATCH 55/60] [CORE-1687] add syncano init command; --- syncano_cli/account/command.py | 4 +-- syncano_cli/account/commands.py | 4 +-- syncano_cli/commands.py | 3 +- syncano_cli/config_commands/commands.py | 4 +++ syncano_cli/hosting/commands.py | 7 ++-- syncano_cli/init/__init__.py | 1 + syncano_cli/init/commands.py | 46 +++++++++++++++++++++++++ syncano_cli/init/helpers.py | 34 ++++++++++++++++++ syncano_cli/instance/command.py | 2 +- syncano_cli/main.py | 6 +++- 10 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 syncano_cli/init/__init__.py create mode 100644 syncano_cli/init/commands.py create mode 100644 syncano_cli/init/helpers.py diff --git a/syncano_cli/account/command.py b/syncano_cli/account/command.py index dd61b5e..b779782 100644 --- a/syncano_cli/account/command.py +++ b/syncano_cli/account/command.py @@ -10,7 +10,7 @@ def __init__(self, config_path): self.connection = syncano.connect() self.config_path = config_path - def register(self, email, password, first_name, last_name, invitation_key): + def register(self, email, password, first_name=None, last_name=None, invitation_key=None): api_key = self.connection.connection().register( email=email, password=password, @@ -19,6 +19,6 @@ def register(self, email, password, first_name, last_name, invitation_key): invitation_key=invitation_key ) - ACCOUNT_CONFIG.set('DEFAULT', 'api_key', api_key) + ACCOUNT_CONFIG.set('DEFAULT', 'key', api_key) with open(self.config_path, 'wt') as fp: ACCOUNT_CONFIG.write(fp) diff --git a/syncano_cli/account/commands.py b/syncano_cli/account/commands.py index e690c09..c40ccb3 100644 --- a/syncano_cli/account/commands.py +++ b/syncano_cli/account/commands.py @@ -12,9 +12,7 @@ def top_account(): @click.pass_context @click.option('--config', help=u'Account configuration file.') def accounts(ctx, config): - """ - Handle Syncano account functionality; - """ + """Handle Syncano account functionality.""" account_commands = AccountCommands(config_path=config or ACCOUNT_CONFIG_PATH) ctx.obj['account_commands'] = account_commands diff --git a/syncano_cli/commands.py b/syncano_cli/commands.py index 0bcc60b..9bedfb2 100644 --- a/syncano_cli/commands.py +++ b/syncano_cli/commands.py @@ -19,8 +19,7 @@ def top_level(): @click.option('--instance-name', help=u'Default instance name.') def login(context, config, instance_name): """ - Log in to syncano using email and password and store ACCOUNT_KEY - in configuration file. + Log in to syncano using email and password. """ config = config or ACCOUNT_CONFIG_PATH context.obj['config'] = config diff --git a/syncano_cli/config_commands/commands.py b/syncano_cli/config_commands/commands.py index 5a6b0d0..68286ec 100644 --- a/syncano_cli/config_commands/commands.py +++ b/syncano_cli/config_commands/commands.py @@ -15,6 +15,7 @@ def top_config(): @click.option('--config', help=u'Account configuration file.') @click.option('--instance-name', help=u'Instance name.') def config(ctx, config, instance_name): + """Allow to manage global instance config.""" instance = get_instance(config, instance_name) config_command = ConfigCommand(instance=instance) ctx.obj['config_command'] = config_command @@ -27,6 +28,7 @@ def config(ctx, config, instance_name): @click.argument('name') @click.argument('value') def add(ctx, name, value): + """Add config variable to global instance config.""" config_command = ctx.obj['config_command'] config_command.add(name, value) @@ -36,6 +38,7 @@ def add(ctx, name, value): @click.argument('name') @click.argument('value') def modify(ctx, name, value): + """Modify config value in global instance config.""" config_command = ctx.obj['config_command'] config_command.modify(name, value) @@ -44,5 +47,6 @@ def modify(ctx, name, value): @click.pass_context @click.argument('name') def delete(ctx, name): + """Removes config value from global instance config.""" config_command = ctx.obj['config_command'] config_command.delete(name) diff --git a/syncano_cli/hosting/commands.py b/syncano_cli/hosting/commands.py index d244c97..568b400 100644 --- a/syncano_cli/hosting/commands.py +++ b/syncano_cli/hosting/commands.py @@ -30,16 +30,15 @@ def hosting(ctx, config, instance_name, domain): @click.pass_context @click.argument('directory') def publish(ctx, directory,): - validate_publish(directory) domain = ctx.obj['domain'] domain = validate_domain(domain) # prepared for user defined domains; hosting_commands = ctx.obj['hosting_commands'] hosting_commands.publish(domain=domain, base_dir=directory) click.echo( - "INFO: Your site published. If default, go to: https://{instance_name}--{domain}.syncano.site. " - "Otherwise, go to: https://{instance_name}.syncano.site".format( - hosting_commands.instance.name, + "INFO: Your site published. If default, go to: https://{instance_name}.syncano.site. " + "Otherwise, go to: https://{instance_name}--{domain}.syncano.site.".format( + instance_name=hosting_commands.instance.name, domain=domain ) ) diff --git a/syncano_cli/init/__init__.py b/syncano_cli/init/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/syncano_cli/init/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/syncano_cli/init/commands.py b/syncano_cli/init/commands.py new file mode 100644 index 0000000..b1a840b --- /dev/null +++ b/syncano_cli/init/commands.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +import click +from syncano_cli.account.command import AccountCommands +from syncano_cli.base.connection import create_connection +from syncano_cli.config import ACCOUNT_CONFIG_PATH +from syncano_cli.init.helpers import random_instance_name +from syncano_cli.instance.command import InstanceCommands + + +@click.group() +def top_init(): + pass + + +@top_init.command() +@click.pass_context +@click.option('--config', help=u'Account configuration file.', default=ACCOUNT_CONFIG_PATH) +@click.option('--email', prompt=True) +@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True) +def init(ctx, config, email, password): + """Register new user and create first instance.""" + # register new account; + account_command = AccountCommands(config_path=config) + account_command.register( + email=email, + password=password, + ) + + # register sum up show; + click.echo() + click.echo('Account created for email: {}'.format(email)) + + # create instance; + connection = create_connection(config) + instance_commands = InstanceCommands(connection) + instance_name = random_instance_name() + instance_commands.create(instance_name=instance_name) + instance_commands.set_default(instance_name=instance_name, config_path=config) + + # instace sum up show; + click.echo() + click.echo('Instance `{}` created.'.format(instance_name)) + + # show instructions; + click.echo() + click.echo(ctx.parent.get_help()) diff --git a/syncano_cli/init/helpers.py b/syncano_cli/init/helpers.py new file mode 100644 index 0000000..957501b --- /dev/null +++ b/syncano_cli/init/helpers.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +import random + +INSTANCE_NAME_ADJECTIVES = [ + 'autumn', 'hidden', 'bitter', 'misty', 'silent', 'empty', 'dry', 'dark', + 'summer', 'icy', 'delicate', 'quiet', 'white', 'cool', 'spring', 'winter', + 'patient', 'twilight', 'dawn', 'crimson', 'wispy', 'weathered', 'blue', + 'billowing', 'broken', 'cold', 'damp', 'falling', 'frosty', 'green', + 'long', 'late', 'lingering', 'bold', 'little', 'morning', 'muddy', 'old', + 'red', 'rough', 'still', 'small', 'sparkling', 'throbbing', 'shy', + 'wandering', 'withered', 'wild', 'black', 'young', 'holy', 'solitary', + 'fragrant', 'aged', 'snowy', 'proud', 'floral', 'restless', 'divine', + 'polished', 'ancient', 'purple', 'lively', 'nameless' +] + +INSTANCE_NAME_NOUNS = [ + 'waterfall', 'river', 'breeze', 'moon', 'rain', 'wind', 'sea', 'morning', + 'snow', 'lake', 'sunset', 'pine', 'shadow', 'leaf', 'dawn', 'glitter', + 'forest', 'hill', 'cloud', 'meadow', 'sun', 'glade', 'bird', 'brook', + 'butterfly', 'bush', 'dew', 'dust', 'field', 'fire', 'flower', 'firefly', + 'feather', 'grass', 'haze', 'mountain', 'night', 'pond', 'darkness', + 'snowflake', 'silence', 'sound', 'sky', 'shape', 'surf', 'thunder', + 'violet', 'water', 'wildflower', 'wave', 'water', 'resonance', 'sun', + 'wood', 'dream', 'cherry', 'tree', 'fog', 'frost', 'voice', 'paper', + 'frog', 'smoke', 'star' +] + + +def random_instance_name(): + return '{adj}-{noun}-{rnd}'.format( + adj=random.choice(INSTANCE_NAME_ADJECTIVES), + noun=random.choice(INSTANCE_NAME_NOUNS), + rnd=random.randint(1000, 9999), + ) diff --git a/syncano_cli/instance/command.py b/syncano_cli/instance/command.py index a3fae5a..eca1ca8 100644 --- a/syncano_cli/instance/command.py +++ b/syncano_cli/instance/command.py @@ -33,7 +33,7 @@ def set_default(cls, instance_name, config_path): with open(config_path, 'wt') as fp: ACCOUNT_CONFIG.write(fp) - def create(self, instance_name, description): + def create(self, instance_name, description=None): kwargs = { 'name': instance_name } diff --git a/syncano_cli/main.py b/syncano_cli/main.py index af4d6e3..1439ee4 100644 --- a/syncano_cli/main.py +++ b/syncano_cli/main.py @@ -11,6 +11,7 @@ from syncano_cli.custom_sockets.commands import top_sockets from syncano_cli.execute.commands import top_execute from syncano_cli.hosting.commands import top_hosting +from syncano_cli.init.commands import top_init from syncano_cli.instance.commands import top_instance from syncano_cli.parse_to_syncano.commands import top_migrate from syncano_cli.sync.commands import top_sync @@ -21,12 +22,15 @@ top_config, top_execute, top_hosting, + top_init, top_instance, top_level, top_migrate, top_sync, top_sockets, - ]) + + ], +) def main(): From 8b2aa01239bab1f16cb4101e085466f9a8d56cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 16:21:48 +0200 Subject: [PATCH 56/60] [CORE-1687] fixes in namings; correct publish info: default or not; --- syncano_cli/hosting/commands.py | 18 ++++++++++++------ syncano_cli/instance/command.py | 4 ++-- syncano_cli/instance/commands.py | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/syncano_cli/hosting/commands.py b/syncano_cli/hosting/commands.py index 568b400..f301e0f 100644 --- a/syncano_cli/hosting/commands.py +++ b/syncano_cli/hosting/commands.py @@ -35,13 +35,19 @@ def publish(ctx, directory,): domain = validate_domain(domain) # prepared for user defined domains; hosting_commands = ctx.obj['hosting_commands'] hosting_commands.publish(domain=domain, base_dir=directory) - click.echo( - "INFO: Your site published. If default, go to: https://{instance_name}.syncano.site. " - "Otherwise, go to: https://{instance_name}--{domain}.syncano.site.".format( - instance_name=hosting_commands.instance.name, - domain=domain + if domain == 'default': + click.echo( + "INFO: Your site published. Go to: https://{instance_name}.syncano.site.".format( + instance_name=hosting_commands.instance.name, + ) + ) + else: + click.echo( + "INFO: Your site published. Go to: https://{instance_name}--{domain}.syncano.site.".format( + instance_name=hosting_commands.instance.name, + domain=domain + ) ) - ) @hosting.group(invoke_without_command=True) diff --git a/syncano_cli/instance/command.py b/syncano_cli/instance/command.py index eca1ca8..6f6a959 100644 --- a/syncano_cli/instance/command.py +++ b/syncano_cli/instance/command.py @@ -52,7 +52,7 @@ def format_details(cls, instance): @classmethod def format_list(cls, instances, default_instance_name): - list_template = """Available instances:{}""" + list_template = """Available Instances:{}""" lines = '' def get_name_label(name, default_instance_name): @@ -62,7 +62,7 @@ def get_name_label(name, default_instance_name): for instance in instances: lines += u"\n\t- {name}: {description}".format( - description=instance.description or u'N/A', + description=instance.description or u'no description', name=get_name_label(instance.name, default_instance_name) ) diff --git a/syncano_cli/instance/commands.py b/syncano_cli/instance/commands.py index fab87bf..b80b070 100644 --- a/syncano_cli/instance/commands.py +++ b/syncano_cli/instance/commands.py @@ -18,7 +18,7 @@ def top_instance(): @click.option('--instance-name', help=u'Instance name.') def instances(ctx, config, instance_name): """ - Mangage your Instances. Instance is an equivalent of a project or a set of data. + Manage your Instances. Instance is an equivalent of a project or a set of data. It contains information about Sockets, Data Classes, Data Objects and more. You can own and/or belong to multiple instances. """ @@ -52,7 +52,7 @@ def details(ctx, instance_name): def delete(ctx, instance_name): instance_name = get_instance_name(ctx.obj['config'], instance_name) # default one if no provided; confirmed_name = click.prompt('Are you sure that you want to delete ' - 'instance {}? Type instance name again'.format(instance_name)) + 'Instance {}? Type instance name again'.format(instance_name)) if confirmed_name == instance_name: ctx.obj['instance_commands'].delete(instance_name) else: From d2d01a5c6606f0421d08d196c026eddabcdc6751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 16:28:04 +0200 Subject: [PATCH 57/60] [CORE-1687] correct README; update docstrings; --- README.rst | 24 ++++++++++++++++++++++++ syncano_cli/instance/commands.py | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 59d4ab6..64c7e6d 100644 --- a/README.rst +++ b/README.rst @@ -58,6 +58,30 @@ If you need to change default instance name, used for all future commands, use:: syncano default name_of_new_default_instance + +If you do not have an Syncano account use `syncano init` command:: + + syncano init + +And follow the steps. CLI will ask you about `email` and `password`, it will also create an Instance for you. +After `syncano init` you can start with getting the list of your Instances:: + + syncano instances list + + +To obtain a help, type:: + + syncano --help + +To display a help for specific command, type:: + + syncano instances --help + +And:: + + syncano instances list --help + + Documentation ============= diff --git a/syncano_cli/instance/commands.py b/syncano_cli/instance/commands.py index b80b070..6e3560e 100644 --- a/syncano_cli/instance/commands.py +++ b/syncano_cli/instance/commands.py @@ -20,7 +20,7 @@ def instances(ctx, config, instance_name): """ Manage your Instances. Instance is an equivalent of a project or a set of data. It contains information about Sockets, Data Classes, Data Objects and more. - You can own and/or belong to multiple instances. + You can own and/or belong to multiple Instances. """ connection = create_connection(config, instance_name) instance_commands = InstanceCommands(connection) From 352436b85eb34fc4c92d911ee7e3a887bbd67c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Mon, 26 Sep 2016 17:04:49 +0200 Subject: [PATCH 58/60] [CORE-1687] correct tests with register; --- tests/test_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_account.py b/tests/test_account.py index d01e0fc..d2932d9 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -23,7 +23,7 @@ def test_register(self): self.runner.invoke(cli, args=['accounts', 'register', email], input='test1234\ntest1234', obj={}) # check if api key is written in the config file; - self.assert_config_variable_exists(ACCOUNT_CONFIG, 'DEFAULT', 'api_key') + self.assert_config_variable_exists(ACCOUNT_CONFIG, 'DEFAULT', 'key') # restore old connection in Syncano LIB; To remove instance in tearDown; self.connection.connection().api_key = old_key From 588e83d4ac1b6206532ecc13e7ef58fbcc22dbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Tue, 27 Sep 2016 20:57:30 +0200 Subject: [PATCH 59/60] [RELEASE v0.8] bump the version; --- README.rst | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 64c7e6d..ef1d28f 100644 --- a/README.rst +++ b/README.rst @@ -135,8 +135,8 @@ Will display custom sockets from `my_instance_name` - because it is set to be a After a registration - there's no default instance set. So it's desired to create one and set it as default:: - syncano instance create my_new_instance - syncano instance default my_new_instance + syncano instances create my_new_instance + syncano instances default my_new_instance It's worth to note that `instance_name` must be unique - but you will get appropriate message if you encounter such case. diff --git a/setup.py b/setup.py index e4e32aa..fd2af01 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='syncano-cli', - version='0.7', + version='0.8', description='Syncano command line utilities', long_description=README, author='Marcin Swiderski, Sebastian Opalczynski', From 48070d6419060b4b6aa339b419008462d542ff63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Opa=C5=82czy=C5=84ski?= Date: Tue, 27 Sep 2016 21:03:22 +0200 Subject: [PATCH 60/60] [RELEASE v0.8] bump python lib version; --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fd2af01..bb05cf9 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ url='https://github.com/Syncano/syncano-cli', packages=find_packages(), license='MIT', - install_requires=['syncano>=5.4.3', 'PyYaml>=3.11', 'watchdog>=0.8.3', 'click>=6.6'], + install_requires=['syncano>=5.4.4', 'PyYaml>=3.11', 'watchdog>=0.8.3', 'click>=6.6'], test_suite='tests', entry_points=""" [console_scripts]