.. _custom-sockets: ========================= Custom Sockets in Syncano ========================= ``Syncano`` gives its users the ability to create Custom Sockets. What this means is that users can define very specific endpoints in their Syncano application, and use them exactly like they would any other Syncano module (Classes, Scripts, etc), using standard API calls. Currently, Custom Sockets allow only one dependency - Scripts. Under the hood, each API call executes a Script, and the result of this execution is returned as a result of the API call. Creating a custom Socket ------------------------ To create a custom Socket follow these steps:: import syncano from syncano.models import CustomSocket, Endpoint, ScriptCall, ScriptDependency, RuntimeChoices from syncano.connection import Connection # 1. Initialize a custom Socket. custom_socket = CustomSocket(name='my_custom_socket') # this will create an object in place (do API call) # 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'])) # What happened here: # - We defined a new endpoint that will be visible under the name `my_endpoint` # - You will be able to call this endpoint (execute attached `call`), # by sending a request, using any defined method to the following API route: # :///instances//endpoints/sockets/my_endpoint/ # - To get details for that endpoint, you need to send a GET request to following API route: # :///instances//sockets/my_custom_socket/endpoints/my_endpoint/ # # Following the example above - we defined two calls on our endpoint with the `add_call` method # The first one means that using a GET method will call the `custom_script` Script, # and second one means that using a POST method will call the `another_custom_script` Script. # At the moment, only Scripts are available as endpoint calls. # # As a general rule - to get endpoint details (but not call them), use following API route: # :///instances//sockets/my_custom_socket/endpoints// # and to run your endpoints (e.g. execute Script connected to them), use following API route: # :///instances//endpoints/sockets// # 3. After creation of the endpoint, add it to your custom_socket. custom_socket.add_endpoint(my_endpoint) # 4. Define dependency. # 4.1 Using a new Script - define a new source code. custom_socket.add_dependency( ScriptDependency( Script( runtime_name=RuntimeChoices.PYTHON_V5_0, source='print("custom_script")' ), name='custom_script' ) ) # 4.2 Using an existing Script. another_custom_script = Script.please.get(id=2) custom_socket.add_dependency( ScriptDependency( another_custom_script, name='another_custom_script', ) ) # 4.3 Using an existing ScriptEndpoint. script_endpoint = ScriptEndpoint.please.get(name='script_endpoint_name') custom_socket.add_dependency( script_endpoint ) # 5. Install custom_socket. custom_socket.install() # this will make an API call and create a script; It may take some time to set up the Socket, so you can check the status. It's possible to check the custom Socket status:: # Reload will refresh object using Syncano API. custom_socket.reload() print(custom_socket.status) # and print(custom_socket.status_info) Updating the custom Socket -------------------------- To update custom Socket, use:: custom_socket = CustomSocket.please.get(name='my_custom_socket') # to remove endpoint/dependency custom_socket.remove_endpoint(endpoint_name='my_endpoint') custom_socket.remove_dependency(dependency_name='custom_script') # or to add a new endpoint/dependency: custom_socket.add_endpoint(new_endpoint) # see above code for endpoint examples; custom_socket.add_dependency(new_dependency) # see above code for dependency examples; # save changes on Syncano custom_socket.update() Running custom Socket ------------------------- To run a custom Socket use:: # this will run `my_endpoint` - and call `custom_script` using GET method; result = custom_socket.run(method='GET', endpoint_name='my_endpoint') Read all endpoints in a custom Socket ----------------------------------- To get the all defined endpoints in a custom Socket run:: endpoints = custom_socket.get_endpoints() for endpoint in endpoints: print(endpoint.name) print(endpoint.calls) To run a particular endpoint:: endpoint.run(method='GET') # or: endpoint.run(method='POST', data={'name': 'test_name'}) Data will be passed to the API call in the request body. Read all endpoints ------------------ To get all endpoints that are defined in all custom Sockets:: socket_endpoint_list = SocketEndpoint.get_all_endpoints() Above code will return a list with SocketEndpoint objects. To run an endpoint, choose one endpoint first, e.g.: endpoint = socket_endpoint_list[0] and now run it:: endpoint.run(method='GET') # or: endpoint.run(method='POST', data={'custom_data': 1}) Custom Sockets endpoints ------------------------ Each custom socket requires defining at least one endpoint. This endpoint is defined by name and a list of calls. Each call is defined by its name and a list of methods. `name` is used as an identification for the dependency, eg. if `name` is equal to 'my_script' - the ScriptEndpoint with name 'my_script' will be used (if it exists and Script source and passed runtime match) -- otherwise a new one will be created. There's a special wildcard method: `methods=['*']` - this allows you to execute the provided custom Socket with any request method (GET, POST, PATCH, etc.). To add an endpoint to a chosen custom_socket use:: 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']) custom_socket.add_endpoint(my_endpoint) Custom Socket dependency ------------------------ Each custom socket has a dependency -- meta information for an endpoint: which resource should be used to return the API call results. These dependencies are bound to the endpoints call object. Currently the only supported dependency is a Script. **Using new Script** :: custom_socket.add_dependency( ScriptDependency( Script( runtime_name=RuntimeChoices.PYTHON_V5_0, source='print("custom_script")' ), name='custom_script' ) ) **Using defined Script** :: another_custom_script = Script.please.get(id=2) custom_socket.add_dependency( ScriptDependency( another_custom_script, name='another_custom_script' ) ) **Using defined Script endpoint** :: script_endpoint = ScriptEndpoint.please.get(name='script_endpoint_name') custom_socket.add_dependency( script_endpoint ) You can overwrite the Script name in the following way:: script_endpoint = ScriptEndpoint.please.get(name='script_endpoint_name') custom_socket.add_dependency( script_endpoint, 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 --------------------- The creation of a Socket can fail - this can happen, for example, when an endpoint name is already taken by another custom Socket. To check the creation status use:: print(custom_socket.status) print(custom_socket.status_info) You can also re-check a Socket. This mean that all dependencies will be checked - if some of them are missing (e.g. some were deleted by mistake), they will be created again. If the endpoints and dependencies do not meet the criteria - an error will be returned in the status field. Custom Socket - install from url -------------------------------- To install a socket from url use:: CustomSocket(name='new_socket_name').install_from_url(url='https://...') If instance name was not provided in connection arguments, do:: CustomSocket(name='new_socket_name').install_from_url(url='https://...', instance_name='instance_name') Custom Socket - raw format -------------------------- If you prefer raw JSON format for creating Sockets, the Python library allows you to do so:::: CustomSocket.please.create( name='my_custom_socket_3', endpoints={ "my_endpoint_3": { "calls": [ {"type": "script", "name": "my_script_3", "methods": ["POST"]} ] } }, dependencies=[ { "type": "script", "runtime_name": "python_library_v5.0", "name": "my_script_3", "source": "print(3)" } ] ) The disadvantage of this method is that the internal structure of the JSON file must be known by the developer.