Skip to content

Commit

Permalink
Merge pull request #247 from Syncano/release-v5.4.3
Browse files Browse the repository at this point in the history
[Release v5.4.3]
  • Loading branch information
opalczynski authored Sep 23, 2016
2 parents 6e7621d + d96df70 commit e7d1f40
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 17 deletions.
31 changes: 29 additions & 2 deletions docs/source/custom_sockets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ To create a custom Socket follow these steps::

# 2. Define endpoints.
my_endpoint = Endpoint(name='my_endpoint') # no API call here
my_endpoint.add_call(ScriptCall(name='custom_script'), methods=['GET'])
my_endpoint.add_call(ScriptCall(name='another_custom_script'), methods=['POST'])
my_endpoint.add_call(ScriptCall(name='custom_script', methods=['GET']))
my_endpoint.add_call(ScriptCall(name='another_custom_script', methods=['POST']))

# What happened here:
# - We defined a new endpoint that will be visible under the name `my_endpoint`
Expand Down Expand Up @@ -224,6 +224,33 @@ You can overwrite the Script name in the following way::
name='custom_name'
)

** Class dependency **

Custom socket with this dependency will check if this class is defined - if not then will create it;
This allows you to define which classes are used to store data for this particular custom socket.

::

custom_socket.add_dependency(
ClassDependency(
Class(
name='class_dep_test',
schema=[
{'name': 'test', 'type': 'string'}
]
),
)
)

Existing class::

class_instance = Class.plase.get(name='user_profile')
custom_socket.add_dependency(
ClassDependency(
class_instance
)
)

Custom Socket recheck
---------------------

Expand Down
2 changes: 1 addition & 1 deletion syncano/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os

__title__ = 'Syncano Python'
__version__ = '5.4.2'
__version__ = '5.4.3'
__author__ = "Daniel Kopka, Michal Kobus and Sebastian Opalczynski"
__credits__ = ["Daniel Kopka",
"Michal Kobus",
Expand Down
2 changes: 1 addition & 1 deletion syncano/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
from .hosting import * # NOQA
from .data_views import DataEndpoint as EndpointData # NOQA
from .custom_sockets import * # NOQA
from .custom_sockets_utils import Endpoint, ScriptCall, ScriptDependency # NOQA
from .custom_sockets_utils import Endpoint, ScriptCall, ScriptDependency, ClassDependency # NOQA
10 changes: 8 additions & 2 deletions syncano/models/custom_sockets.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CustomSocket(EndpointMetadataMixin, DependencyMetadataMixin, Model):
endpoints = fields.JSONField()
dependencies = fields.JSONField()
metadata = fields.JSONField(required=False)
config = fields.JSONField(required=False)
status = fields.StringField(read_only=True, required=False)
status_info = fields.StringField(read_only=True, required=False)
created_at = fields.DateTimeField(read_only=True, required=False)
Expand Down Expand Up @@ -58,13 +59,18 @@ def _find_endpoint(self, endpoint_name):
return endpoint
raise SyncanoValueError('Endpoint {} not found.'.format(endpoint_name))

def install_from_url(self, url, instance_name=None):
def install_from_url(self, url, instance_name=None, config=None):
instance_name = self.__class__.please.properties.get('instance_name') or instance_name
instance = Instance.please.get(name=instance_name)

install_path = instance.links.sockets_install
connection = self._get_connection()
response = connection.request('POST', install_path, data={'name': self.name, 'install_url': url})
config = config or {}
response = connection.request('POST', install_path, data={
'name': self.name,
'install_url': url,
'config': config
})

return response

Expand Down
55 changes: 46 additions & 9 deletions syncano/models/custom_sockets_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import six
from syncano.exceptions import SyncanoValueError

from .classes import Class
from .incentives import Script, ScriptEndpoint


Expand All @@ -17,6 +18,7 @@ class DependencyType(object):
The type of the dependency object used in the custom socket;
"""
SCRIPT = 'script'
CLASS = 'class'


class BaseCall(object):
Expand Down Expand Up @@ -109,14 +111,19 @@ def to_dependency_data(self):
return dependency_data

def get_name(self):
raise NotImplementedError()
if self.name is not None:
return {'name': self.name}
return {'name': self.dependency_object.name}

def get_dependency_data(self):
raise NotImplementedError()

def create_from_raw_data(self, raw_data):
raise NotImplementedError()

def _build_dict(self, instance):
return {field_name: getattr(instance, field_name) for field_name in self.fields}


class ScriptDependency(BaseDependency):
"""
Expand Down Expand Up @@ -147,11 +154,6 @@ def __init__(self, script_or_script_endpoint, name=None):
self.dependency_object = script_or_script_endpoint
self.name = name

def get_name(self):
if self.name is not None:
return {'name': self.name}
return {'name': self.dependency_object.name}

def get_dependency_data(self):

if isinstance(self.dependency_object, ScriptEndpoint):
Expand All @@ -161,9 +163,7 @@ def get_dependency_data(self):
script = self.dependency_object

dependency_data = self.get_name()
dependency_data.update({
field_name: getattr(script, field_name) for field_name in self.fields
})
dependency_data.update(self._build_dict(script))
return dependency_data

@classmethod
Expand All @@ -174,6 +174,41 @@ def create_from_raw_data(cls, raw_data):
})


class ClassDependency(BaseDependency):
"""
Class dependency object;
The JSON format is as follows::
{
'type': 'class',
'name': '<class_name>',
'schema': [
{"name": "f1", "type": "string"},
{"name": "f2", "type": "string"},
{"name": "f3", "type": "integer"}
],
}
"""
dependency_type = DependencyType.CLASS
fields = [
'name',
'schema'
]

def __init__(self, class_instance):
self.dependency_object = class_instance
self.name = class_instance.name

def get_dependency_data(self):
data_dict = self._build_dict(self.dependency_object)
data_dict['schema'] = data_dict['schema'].schema
return data_dict

@classmethod
def create_from_raw_data(cls, raw_data):
return cls(**{'class_instance': Class(**raw_data)})


class EndpointMetadataMixin(object):
"""
A mixin which allows to collect Endpoints objects and transform them to the appropriate JSON format.
Expand Down Expand Up @@ -239,6 +274,8 @@ def update_dependencies(self):
def _get_depedency_klass(cls, depedency_type):
if depedency_type == DependencyType.SCRIPT:
return ScriptDependency
elif depedency_type == DependencyType.CLASS:
return ClassDependency

def add_dependency(self, depedency):
self._dependencies.append(depedency)
Expand Down
4 changes: 2 additions & 2 deletions tests/integration_test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def setUpClass(cls):
cls.INSTANCE_NAME = os.getenv('INTEGRATION_INSTANCE_NAME')
cls.USER_NAME = os.getenv('INTEGRATION_USER_NAME')
cls.USER_PASSWORD = os.getenv('INTEGRATION_USER_PASSWORD')
cls.CLASS_NAME = cls.INSTANCE_NAME
cls.CLASS_NAME = "login_class_test"

instance = cls.connection.Instance.please.create(name=cls.INSTANCE_NAME)
api_key = instance.api_keys.create(allow_user_create=True,
Expand All @@ -35,7 +35,7 @@ def tearDownClass(cls):
cls.connection = None

def check_connection(self, con):
response = con.request('GET', '/v1.1/instances/test_login/classes/')
response = con.request('GET', '/v1.1/instances/{}/classes/'.format(self.INSTANCE_NAME))

obj_list = response['objects']

Expand Down
64 changes: 64 additions & 0 deletions tests/integration_test_custom_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import time

from syncano.models import (
Class,
ClassDependency,
CustomSocket,
Endpoint,
RuntimeChoices,
Expand Down Expand Up @@ -44,6 +46,14 @@ def test_creating_raw_data(self):
"runtime_name": "python_library_v5.0",
"name": "script_123",
"source": "print(123)"
},
{
"type": "class",
"name": "klass",
"schema": [
{"name": "fieldA", "type": "string"},
{"name": "fieldB", "type": "integer"},
]
}
]
)
Expand Down Expand Up @@ -91,6 +101,16 @@ def test_custom_socket_update(self):
socket_to_update.reload()
self.assertIn('my_endpoint_new_to_update', socket_to_update.endpoints)

def test_class_dependency_new(self):
suffix = 'new_class'
custom_socket = self._create_custom_socket(suffix, self._define_dependencies_new_class)
self._assert_custom_socket(custom_socket)

def test_class_dependency_existing(self):
suffix = 'existing_class'
custom_socket = self._create_custom_socket(suffix, self._define_dependencies_new_class)
self._assert_custom_socket(custom_socket)

def assert_custom_socket(self, suffix, dependency_method):
custom_socket = self._create_custom_socket(suffix, dependency_method=dependency_method)
self._assert_custom_socket(custom_socket)
Expand Down Expand Up @@ -122,6 +142,38 @@ def _define_endpoints(cls, suffix, custom_socket):
)
custom_socket.add_endpoint(endpoint)

@classmethod
def _define_dependencies_new_class(cls, suffix, custom_socket):
cls._add_base_script(suffix, custom_socket)
custom_socket.add_dependency(
ClassDependency(
Class(
name="test_class_{}".format(suffix),
schema=[
{"name": "testA", "type": "string"},
{"name": "testB", "type": "integer"},
]
)
)
)

@classmethod
def _define_dependencies_existing_class(cls, suffix, custom_socket):
cls._add_base_script(suffix, custom_socket)
klass = Class(
name="test_class_{}".format(suffix),
schema=[
{"name": "testA", "type": "string"},
{"name": "testB", "type": "integer"},
]
)
klass.save()
custom_socket.add_dependency(
ClassDependency(
klass
)
)

@classmethod
def _define_dependencies_new_script_endpoint(cls, suffix, custom_socket):
script = cls._create_script(suffix)
Expand Down Expand Up @@ -171,6 +223,18 @@ def _define_dependencies_existing_script_endpoint(cls, suffix, custom_socket):
)
)

@classmethod
def _add_base_script(cls, suffix, custom_socket):
custom_socket.add_dependency(
ScriptDependency(
Script(
source='print("{}")'.format(suffix),
runtime_name=RuntimeChoices.PYTHON_V5_0
),
name='script_endpoint_{}'.format(suffix),
)
)

@classmethod
def _create_script(cls, suffix):
return Script.please.create(
Expand Down

0 comments on commit e7d1f40

Please sign in to comment.