@slingr/slingr-services 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 SLINGR
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,373 @@
1
+ ---
2
+ title: Services Nodejs SDK
3
+ keywords:
4
+ last_updated: Nov 1, 2022
5
+ tags: []
6
+ summary: "Nodejs SDK to create Slingr Services."
7
+ sidebar: extensions_sidebar
8
+ permalink: extensions_node_sdk.html
9
+ folder: extensions
10
+ ---
11
+
12
+ This document will guide through the creation of a service using the Nodejs SDK and will provide details about the
13
+ framework.
14
+
15
+ # Create your new service project
16
+
17
+ ## package.json file
18
+
19
+ The `package.json` contains a few things that you may want to modify:
20
+
21
+ - `name`: This a human-readable name for your service.
22
+ - `version`: This is the version of your service. You can leave `1.0.0` as this version has nothing
23
+ to do with the versions registered in SLINGR, which uses the tags in your repository instead.
24
+ - `description`: Description of your what this service is about.
25
+ - `keywords`: Here you can set some keywords related to your service.
26
+
27
+ ## Service descriptor
28
+
29
+ The file `service.json` contains at least two fields that you will want to update:
30
+
31
+ - `label`: this is the human-friendly name of the service.
32
+ - `name`: this is the internal name of the service and must match the name you use to register the service
33
+ in SLINGR.
34
+
35
+ To understand the other settings, you want to take a look at [Services features]({{site.baseurl}}/extensions_common_features.html).
36
+
37
+ ## Reading configuration
38
+
39
+ You can access the service configuration like this (always inside a function):
40
+
41
+ ```js
42
+ svc.functions.someFunction = (svcRequest) => {
43
+ const configs = svc.serviceConfig;
44
+ //your code...
45
+ }
46
+ ```
47
+
48
+ ## Hooks
49
+
50
+ There are a few hooks in services that you can use to perform some initializations or clean up.
51
+
52
+ ```js
53
+ svc.hooks.onConfigurationReady = () => {
54
+ //Some code here...
55
+ }
56
+ svc.hooks.onSvcServicesConfigured = () => {
57
+ //Some code here...
58
+ }
59
+ svc.hooks.onWebServicesReady = () => {
60
+ //Some code here...
61
+ }
62
+ svc.hooks.onSvcStart = () => {
63
+ //Some code here...
64
+ }
65
+ //This one receives a 'cause' parameter wich is the 'code' of the process.on('beforeExit') event
66
+ svc.hooks.onSvcStop = (cause) => {
67
+ //Some code here...
68
+ }
69
+ ```
70
+
71
+ ## Functions
72
+
73
+ To implement a function that is defined in the `service.json` file, you should do the following:
74
+
75
+ ```js
76
+ svc.functions.yourFunctionName = (svcRequest) => {
77
+ //You can access all the service services here like svc.serviceConfig or svc.dataStores
78
+ //Your custom code goes here...
79
+ return { someInfo: 'someValue'}
80
+ }
81
+ ```
82
+ Service functions will get a request parameter that includes the parameters sent to the function (among other info).
83
+ You must always return a `Json` object with the response.
84
+
85
+ ## Events
86
+
87
+ You can send events to the app using the events' property of the service.
88
+ You can send an async event, or a sync one if you expect a response from the app:
89
+
90
+ ```js
91
+ svc.functions.fnThatSendsAsyncEvent = (svcRequest) => {
92
+ const requestId = svcRequest.id;
93
+ //Later in your code...
94
+ svcs.events.send('someEventName', data, requestId);
95
+ }
96
+
97
+ svc.functions.fnThatSendsSyncEvent = (svcRequest) => {
98
+ const requestId = svcRequest.id;
99
+ //Later in your code...
100
+ let eventResponse = svcs.events.sendSync('someEventName', data, requestId);
101
+ //Do something with that response...
102
+ }
103
+ ```
104
+
105
+ Keep in mind that the `'someEventName'` should be defined in your `service.json` file, under the `events` property.
106
+ The `data` argument will be the data you want to receive on the event.
107
+ Finally, `requestId` will be the request id which can be retrieved from the request parameter on the defined function like shown above.
108
+
109
+ ## Data stores
110
+
111
+ If you service needs to persist information, data stores are available for services. They need to be defined in the
112
+ `service.json` file and then you can use them in the service.
113
+
114
+ The available methods to access the various datastores are the following:
115
+
116
+ ```js
117
+ svcs.functions.someFunction = async () => {
118
+ //Find documents by some flter: Filter here is the same as the one in sys.data.find(). eg: {someField: 'someValue'}
119
+ svcs.dataStores.someDataStore.find(filter);
120
+ svcs.dataStores.someDataStore.findOne(filter);
121
+ //Find document by id:
122
+ svcs.dataStores.someDataStore.findById('documentId');
123
+ //Save a document: Some object will be any javascript object.
124
+ svcs.dataStores.someDataStore.save(someObject);
125
+ //Update a document:
126
+ svcs.dataStores.someDataStore.update('documentId',someObject);
127
+ //Remove documents by filter:
128
+ svcs.dataStores.someDataStore.remove(filter);
129
+ //Remove by id:
130
+ svcs.dataStores.someDataStore.removeById('documentId',someObject);
131
+ //Count the documents currently saved in the store by filter:
132
+ svcs.dataStores.someDataStore.count(filter);
133
+ }
134
+ ```
135
+ You can either `await` the response or use the `then()` block depending on your needs.
136
+
137
+ ## Webservices
138
+
139
+ If you want your service to receive calls over HTTP, you can define them with the `webServices` property. You must define it
140
+ as an object which will contain the `method`, `path` and `handler` of the webservice:
141
+
142
+ ```js
143
+ svc.webServices.nameForYourWebService = {
144
+ method: 'POST',
145
+ path: '/',
146
+ //As this is an express service, you receive the req, and res objects
147
+ handler: (req, res) => {
148
+ //Do something... and then return a response to the caller
149
+ res.json({status: 'ok'})
150
+ }
151
+ }
152
+ ```
153
+
154
+ Given the above example, the following URL will be available and listening to requests:
155
+
156
+ ```
157
+ POST https://<yourAppName>.slingrs.io/<env>/svcs/<svcName>
158
+ ```
159
+
160
+ When that URL is called, the handler will be invoked.
161
+
162
+ {% include callout.html content="You should always add some kind of verification (like a token) to avoid anyone calling your services." type="warning" %}
163
+
164
+ ## Handling files
165
+
166
+ It is possible to upload and download files to/from the app using the utilities in the property `files`.
167
+ If you want to handle the processing in a sync or async way, you will need to either `await` the service, or handle the
168
+ response in the `then()` block and send an event to the platform.
169
+
170
+ Both scenarios are shown below:
171
+
172
+ ```js
173
+ svc.functions.asyncDownloadFileFromSvc = async (svcRequest) => {
174
+ const file = svcRequest.params;
175
+ svc.files.download(file.id).then(
176
+ (res) => {
177
+ svc.logger.info('File download has completed!');
178
+ //In this case we return res.toString() because we know the file being downloaded is a .txt. Its not recommended to return the plain buffer to the platform.
179
+ svc.events.send('onDownloadComplete', res.toString(), svcRequest.id)
180
+ }
181
+ );
182
+ return { msg: 'File [' + file.id + '] is being downloaded and the processing will be made asynchronously. An event will be fired when the download is complete.' }
183
+ };
184
+
185
+ svc.functions.syncDownloadFileFromSvc = async (svcRequest) => {
186
+ const file = svcRequest.params;
187
+ var fileResponse = await svc.files.download(file.id);
188
+ svc.logger.info('File download has completed!');
189
+ //In this case we return res.toString() because we know the file being downloaded is a .txt. Its not recommended to return the plain buffer to the platform.
190
+ return { fileContents: fileResponse.toString() }
191
+ };
192
+
193
+ svc.functions.uploadFileSyncFromSvc = async (svcRequest) => {
194
+ const fileUrl = 'https://jsoncompare.org/LearningContainer/SampleFiles/PDF/sample-pdf-with-images.pdf';
195
+ try {
196
+ //We download the dummy file from an HTTP request
197
+ var downloadResponse = await svc.httpModule.get(fileUrl);
198
+ } catch (error) {
199
+ svc.logger.error('Couldn\'t download the file from [' + fileUrl + '].', error);
200
+ }
201
+ //And upload it to the platform
202
+ var fileInfo = await svc.files.upload('somefile.pdf', downloadResponse.data);
203
+ //The info is returned to the app synchronously
204
+ return fileInfo;
205
+ };
206
+
207
+ svc.functions.uploadFileAsyncFromSvc = (svcRequest) => {
208
+
209
+ const fileUrl = 'https://jsoncompare.org/LearningContainer/SampleFiles/PDF/sample-pdf-with-images.pdf';
210
+ //We download the dummy file from an HTTP request
211
+ svc.httpModule.get(fileUrl).then(
212
+ (downloadResponse) => {
213
+ //And upload it to the platform
214
+ svc.files.upload('somefile.pdf', downloadResponse.data).then(
215
+ (fileInfo) => {
216
+ //In this case, the info will be sent asynchronously via events
217
+ svc.events.send('onUploadComplete', fileInfo, svcRequest.id);
218
+ }
219
+ ).catch(
220
+ (err) => {
221
+ svc.logger.error('Couldn\'t upload the file to platform.', err);
222
+ }
223
+ );
224
+ }
225
+ ).catch(
226
+ (err) => {
227
+ svc.logger.error('Couldn\'t download the file from [' + fileUrl + '].', err);
228
+ }
229
+ );
230
+
231
+ return { msg: 'A file will be downloaded and then uploaded to the platform. This processing will be made asynchronously. An event will be fired when the download/upload is complete.' }
232
+ };
233
+ ```
234
+
235
+ {% include important.html content="Remember that the events must be defined in the **`service.json`** file, and if you are using callbacks, also in the function's callbacks array property."%}
236
+
237
+ ## Logging
238
+
239
+ It is possible to send logs to the app from your service using the `AppLogs`:
240
+
241
+ ```js
242
+ svc.functions.someFunctionThatLogs = (svcRequest) => {
243
+ svc.appLogger.debug('Function executed!')
244
+ svc.appLogger.info('Function executed!')
245
+ svc.appLogger.warn('Function executed!')
246
+ svc.appLogger.error('Function executed!')
247
+ }
248
+ ```
249
+
250
+ You can send additional information that will be displayed when you click on `More Info` in the log in the app monitor by sending
251
+ a second parameter to the appLogger functions like this:
252
+
253
+ ```js
254
+ svc.functions.someFunctionThatLogs = (svcRequest) => {
255
+ svc.appLogger.debug('Function executed!',someObjectOrMessage)
256
+ svc.appLogger.info('Function executed!',someObjectOrMessage)
257
+ svc.appLogger.warn('Function executed!',someObjectOrMessage)
258
+ svc.appLogger.error('Function executed!',someObjectOrMessage)
259
+ }
260
+ ```
261
+
262
+ *Debug logs will only be shown in dev and staging environments monitor.*
263
+
264
+ # Creating a proxy service
265
+
266
+ Before you can run your service locally, you should set up a proxy service in the app you will be using to test
267
+ the development of your service. You can find more information about this in [Create your own services]({{site.baseurl}}/extensions_create_your_own_services.html).
268
+
269
+ When you add a new `Proxy service` to you app, you will be asked to enter the `Service URI` in the configuration. We
270
+ recommend to use [ngrok](https://ngrok.com/) instead of opening a port in your router. With `ngrok` you can set up
271
+ a URI like this:
272
+
273
+ ```
274
+ ./ngrok http 10000
275
+ ```
276
+
277
+ This will give you an HTTP and HTTPS URL. We recommend using the HTTPS URL, so copy it into the configuration of
278
+ your service.
279
+
280
+ Regarding the token we recommend to leave the autogenerated token, except that you have a reason not to do that.
281
+
282
+ Once you create the service, you will see the configuration below, something like this:
283
+
284
+ ```
285
+ _svc_name=proxy
286
+ _app_name=yourtestapp
287
+ _environment=dev
288
+ _pod_id=id
289
+ _profile=default
290
+ _custom_domain=
291
+ _debug=true
292
+ _local_deployment=true
293
+ _base_domain=slingrs.io
294
+ _webservices_port=10000
295
+ _svcs_services_api=https://yourtestapp.slingrs.io/dev/svcs/proxy/api
296
+ _token=91833a8b-929f-4eab-b7b4-2383c10cd629
297
+ _service_config={}
298
+ ```
299
+
300
+ You should copy this configuration to `.env` file. Keep in mind that the last property, `_service_config`,
301
+ should have a valid JSON with the config of your service, so you might want to override that.
302
+ If you used the skeleton service you should have something like this:
303
+
304
+ ```
305
+ _svc_name=proxy
306
+ _app_name=yourtestapp
307
+ _environment=dev
308
+ _pod_id=id
309
+ _profile=default
310
+ _custom_domain=
311
+ _debug=true
312
+ _local_deployment=true
313
+ _base_domain=slingrs.io
314
+ _webservices_port=10000
315
+ _svcs_services_api=https://yourtestapp.slingrs.io/dev/svcs/proxy/api
316
+ _token=91833a8b-929f-4eab-b7b4-2383c10cd629
317
+ _service_config={"token":"123456"}
318
+ ```
319
+
320
+ Keep in mind that `.env` is only used when you run the service locally, but it does not affect the service when running on the cloud because service config is passed in another way.
321
+
322
+ *If you need, you can have multiple `.env` for different environments or setups. For example, you could have additional `.staging.env` or `.myCustomEnv.env` files. In this case, you should execute your service setting the `NODE_ENV` environment variable to match the name of the file. [Here it's how set env variables in different OSs and terminals](https://stackoverflow.com/a/9204973).
323
+ By default, the `.env` file is loaded.*
324
+
325
+ Once you have created the proxy service in your app, remember to push changes to initialize it.
326
+
327
+ # Running your service
328
+
329
+ Before running your service, make sure that install all the dependencies:
330
+
331
+ ```
332
+ cd SERVICE_FOLDER
333
+ npm install
334
+ ```
335
+
336
+ Then you can run your service from the command line or using your IDE:
337
+
338
+ ```
339
+ node service.js
340
+ ```
341
+ or
342
+ ```
343
+ npm start
344
+ ```
345
+ Or you can customize your own start script from the `package.json` file.
346
+
347
+ # Testing that your service is working
348
+
349
+ Now that the service is running and the proxy service is set up, we can do a quick test to verify everything
350
+ is working. In order to do that execute the following code in your builder or monitor console:
351
+
352
+ ```js
353
+ var res = app.svcs.proxy.randomNumber({});
354
+ log('res: '+JSON.stringify(res));
355
+ ```
356
+
357
+ You should see an output like this:
358
+
359
+ ```
360
+ res: {"number":5560}
361
+ ```
362
+
363
+ We are assuming that you are using the skeleton service template where this method is available. Otherwise,
364
+ you should call a method that exists in your service.
365
+
366
+ # More samples
367
+
368
+ There are dozens of services already developed for the SLINGR platform. You can take a look at them to see
369
+ more features in the services' framework:
370
+
371
+ [https://github.com/slingr-stack](https://github.com/slingr-stack)
372
+
373
+ {% include links.html %}