Skip to content

Commit e2418a5

Browse files
committed
[LIB-898] add class dependency to the library; add config field to CustomSocket;
1 parent f9723dc commit e2418a5

File tree

5 files changed

+140
-12
lines changed

5 files changed

+140
-12
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/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: 1 addition & 0 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)

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_custom_socket.py

Lines changed: 63 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,13 @@ 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+
"schema": [
53+
{"name": "test_class", "type": "string"},
54+
{"name": "test_class", "type": "int"},
55+
]
4756
}
4857
]
4958
)
@@ -91,6 +100,16 @@ def test_custom_socket_update(self):
91100
socket_to_update.reload()
92101
self.assertIn('my_endpoint_new_to_update', socket_to_update.endpoints)
93102

103+
def test_class_dependency_new(self):
104+
suffix = 'new_class'
105+
custom_socket = self._create_custom_socket(suffix, self._define_dependencies_new_class)
106+
self._assert_custom_socket(custom_socket)
107+
108+
def test_class_dependency_existing(self):
109+
suffix = 'existing_class'
110+
custom_socket = self._create_custom_socket(suffix, self._define_dependencies_new_class)
111+
self._assert_custom_socket(custom_socket)
112+
94113
def assert_custom_socket(self, suffix, dependency_method):
95114
custom_socket = self._create_custom_socket(suffix, dependency_method=dependency_method)
96115
self._assert_custom_socket(custom_socket)
@@ -122,6 +141,38 @@ def _define_endpoints(cls, suffix, custom_socket):
122141
)
123142
custom_socket.add_endpoint(endpoint)
124143

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

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

0 commit comments

Comments
 (0)