Skip to content

Commit e7d1f40

Browse files
authored
Merge pull request #247 from Syncano/release-v5.4.3
[Release v5.4.3]
2 parents 6e7621d + d96df70 commit e7d1f40

File tree

7 files changed

+151
-17
lines changed

7 files changed

+151
-17
lines changed

docs/source/custom_sockets.rst

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ To create a custom Socket follow these steps::
2525

2626
# 2. Define endpoints.
2727
my_endpoint = Endpoint(name='my_endpoint') # no API call here
28-
my_endpoint.add_call(ScriptCall(name='custom_script'), methods=['GET'])
29-
my_endpoint.add_call(ScriptCall(name='another_custom_script'), methods=['POST'])
28+
my_endpoint.add_call(ScriptCall(name='custom_script', methods=['GET']))
29+
my_endpoint.add_call(ScriptCall(name='another_custom_script', methods=['POST']))
3030

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

227+
** Class dependency **
228+
229+
Custom socket with this dependency will check if this class is defined - if not then will create it;
230+
This allows you to define which classes are used to store data for this particular custom socket.
231+
232+
::
233+
234+
custom_socket.add_dependency(
235+
ClassDependency(
236+
Class(
237+
name='class_dep_test',
238+
schema=[
239+
{'name': 'test', 'type': 'string'}
240+
]
241+
),
242+
)
243+
)
244+
245+
Existing class::
246+
247+
class_instance = Class.plase.get(name='user_profile')
248+
custom_socket.add_dependency(
249+
ClassDependency(
250+
class_instance
251+
)
252+
)
253+
227254
Custom Socket recheck
228255
---------------------
229256

syncano/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33

44
__title__ = 'Syncano Python'
5-
__version__ = '5.4.2'
5+
__version__ = '5.4.3'
66
__author__ = "Daniel Kopka, Michal Kobus and Sebastian Opalczynski"
77
__credits__ = ["Daniel Kopka",
88
"Michal Kobus",

syncano/models/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
from .hosting import * # NOQA
1515
from .data_views import DataEndpoint as EndpointData # NOQA
1616
from .custom_sockets import * # NOQA
17-
from .custom_sockets_utils import Endpoint, ScriptCall, ScriptDependency # NOQA
17+
from .custom_sockets_utils import Endpoint, ScriptCall, ScriptDependency, ClassDependency # NOQA

syncano/models/custom_sockets.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class CustomSocket(EndpointMetadataMixin, DependencyMetadataMixin, Model):
2525
endpoints = fields.JSONField()
2626
dependencies = fields.JSONField()
2727
metadata = fields.JSONField(required=False)
28+
config = fields.JSONField(required=False)
2829
status = fields.StringField(read_only=True, required=False)
2930
status_info = fields.StringField(read_only=True, required=False)
3031
created_at = fields.DateTimeField(read_only=True, required=False)
@@ -58,13 +59,18 @@ def _find_endpoint(self, endpoint_name):
5859
return endpoint
5960
raise SyncanoValueError('Endpoint {} not found.'.format(endpoint_name))
6061

61-
def install_from_url(self, url, instance_name=None):
62+
def install_from_url(self, url, instance_name=None, config=None):
6263
instance_name = self.__class__.please.properties.get('instance_name') or instance_name
6364
instance = Instance.please.get(name=instance_name)
6465

6566
install_path = instance.links.sockets_install
6667
connection = self._get_connection()
67-
response = connection.request('POST', install_path, data={'name': self.name, 'install_url': url})
68+
config = config or {}
69+
response = connection.request('POST', install_path, data={
70+
'name': self.name,
71+
'install_url': url,
72+
'config': config
73+
})
6874

6975
return response
7076

syncano/models/custom_sockets_utils.py

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import six
33
from syncano.exceptions import SyncanoValueError
44

5+
from .classes import Class
56
from .incentives import Script, ScriptEndpoint
67

78

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

2123

2224
class BaseCall(object):
@@ -109,14 +111,19 @@ def to_dependency_data(self):
109111
return dependency_data
110112

111113
def get_name(self):
112-
raise NotImplementedError()
114+
if self.name is not None:
115+
return {'name': self.name}
116+
return {'name': self.dependency_object.name}
113117

114118
def get_dependency_data(self):
115119
raise NotImplementedError()
116120

117121
def create_from_raw_data(self, raw_data):
118122
raise NotImplementedError()
119123

124+
def _build_dict(self, instance):
125+
return {field_name: getattr(instance, field_name) for field_name in self.fields}
126+
120127

121128
class ScriptDependency(BaseDependency):
122129
"""
@@ -147,11 +154,6 @@ def __init__(self, script_or_script_endpoint, name=None):
147154
self.dependency_object = script_or_script_endpoint
148155
self.name = name
149156

150-
def get_name(self):
151-
if self.name is not None:
152-
return {'name': self.name}
153-
return {'name': self.dependency_object.name}
154-
155157
def get_dependency_data(self):
156158

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

163165
dependency_data = self.get_name()
164-
dependency_data.update({
165-
field_name: getattr(script, field_name) for field_name in self.fields
166-
})
166+
dependency_data.update(self._build_dict(script))
167167
return dependency_data
168168

169169
@classmethod
@@ -174,6 +174,41 @@ def create_from_raw_data(cls, raw_data):
174174
})
175175

176176

177+
class ClassDependency(BaseDependency):
178+
"""
179+
Class dependency object;
180+
181+
The JSON format is as follows::
182+
{
183+
'type': 'class',
184+
'name': '<class_name>',
185+
'schema': [
186+
{"name": "f1", "type": "string"},
187+
{"name": "f2", "type": "string"},
188+
{"name": "f3", "type": "integer"}
189+
],
190+
}
191+
"""
192+
dependency_type = DependencyType.CLASS
193+
fields = [
194+
'name',
195+
'schema'
196+
]
197+
198+
def __init__(self, class_instance):
199+
self.dependency_object = class_instance
200+
self.name = class_instance.name
201+
202+
def get_dependency_data(self):
203+
data_dict = self._build_dict(self.dependency_object)
204+
data_dict['schema'] = data_dict['schema'].schema
205+
return data_dict
206+
207+
@classmethod
208+
def create_from_raw_data(cls, raw_data):
209+
return cls(**{'class_instance': Class(**raw_data)})
210+
211+
177212
class EndpointMetadataMixin(object):
178213
"""
179214
A mixin which allows to collect Endpoints objects and transform them to the appropriate JSON format.
@@ -239,6 +274,8 @@ def update_dependencies(self):
239274
def _get_depedency_klass(cls, depedency_type):
240275
if depedency_type == DependencyType.SCRIPT:
241276
return ScriptDependency
277+
elif depedency_type == DependencyType.CLASS:
278+
return ClassDependency
242279

243280
def add_dependency(self, depedency):
244281
self._dependencies.append(depedency)

tests/integration_test_accounts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def setUpClass(cls):
1414
cls.INSTANCE_NAME = os.getenv('INTEGRATION_INSTANCE_NAME')
1515
cls.USER_NAME = os.getenv('INTEGRATION_USER_NAME')
1616
cls.USER_PASSWORD = os.getenv('INTEGRATION_USER_PASSWORD')
17-
cls.CLASS_NAME = cls.INSTANCE_NAME
17+
cls.CLASS_NAME = "login_class_test"
1818

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

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

4040
obj_list = response['objects']
4141

tests/integration_test_custom_socket.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import time
33

44
from syncano.models import (
5+
Class,
6+
ClassDependency,
57
CustomSocket,
68
Endpoint,
79
RuntimeChoices,
@@ -44,6 +46,14 @@ def test_creating_raw_data(self):
4446
"runtime_name": "python_library_v5.0",
4547
"name": "script_123",
4648
"source": "print(123)"
49+
},
50+
{
51+
"type": "class",
52+
"name": "klass",
53+
"schema": [
54+
{"name": "fieldA", "type": "string"},
55+
{"name": "fieldB", "type": "integer"},
56+
]
4757
}
4858
]
4959
)
@@ -91,6 +101,16 @@ def test_custom_socket_update(self):
91101
socket_to_update.reload()
92102
self.assertIn('my_endpoint_new_to_update', socket_to_update.endpoints)
93103

104+
def test_class_dependency_new(self):
105+
suffix = 'new_class'
106+
custom_socket = self._create_custom_socket(suffix, self._define_dependencies_new_class)
107+
self._assert_custom_socket(custom_socket)
108+
109+
def test_class_dependency_existing(self):
110+
suffix = 'existing_class'
111+
custom_socket = self._create_custom_socket(suffix, self._define_dependencies_new_class)
112+
self._assert_custom_socket(custom_socket)
113+
94114
def assert_custom_socket(self, suffix, dependency_method):
95115
custom_socket = self._create_custom_socket(suffix, dependency_method=dependency_method)
96116
self._assert_custom_socket(custom_socket)
@@ -122,6 +142,38 @@ def _define_endpoints(cls, suffix, custom_socket):
122142
)
123143
custom_socket.add_endpoint(endpoint)
124144

145+
@classmethod
146+
def _define_dependencies_new_class(cls, suffix, custom_socket):
147+
cls._add_base_script(suffix, custom_socket)
148+
custom_socket.add_dependency(
149+
ClassDependency(
150+
Class(
151+
name="test_class_{}".format(suffix),
152+
schema=[
153+
{"name": "testA", "type": "string"},
154+
{"name": "testB", "type": "integer"},
155+
]
156+
)
157+
)
158+
)
159+
160+
@classmethod
161+
def _define_dependencies_existing_class(cls, suffix, custom_socket):
162+
cls._add_base_script(suffix, custom_socket)
163+
klass = Class(
164+
name="test_class_{}".format(suffix),
165+
schema=[
166+
{"name": "testA", "type": "string"},
167+
{"name": "testB", "type": "integer"},
168+
]
169+
)
170+
klass.save()
171+
custom_socket.add_dependency(
172+
ClassDependency(
173+
klass
174+
)
175+
)
176+
125177
@classmethod
126178
def _define_dependencies_new_script_endpoint(cls, suffix, custom_socket):
127179
script = cls._create_script(suffix)
@@ -171,6 +223,18 @@ def _define_dependencies_existing_script_endpoint(cls, suffix, custom_socket):
171223
)
172224
)
173225

226+
@classmethod
227+
def _add_base_script(cls, suffix, custom_socket):
228+
custom_socket.add_dependency(
229+
ScriptDependency(
230+
Script(
231+
source='print("{}")'.format(suffix),
232+
runtime_name=RuntimeChoices.PYTHON_V5_0
233+
),
234+
name='script_endpoint_{}'.format(suffix),
235+
)
236+
)
237+
174238
@classmethod
175239
def _create_script(cls, suffix):
176240
return Script.please.create(

0 commit comments

Comments
 (0)