@sw-tsdk/plugin-connector 3.13.1 → 3.13.2-next.3dfd44a

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.
Files changed (74) hide show
  1. package/README.md +18 -18
  2. package/lib/commands/connector/build.js +168 -44
  3. package/lib/commands/connector/build.js.map +1 -1
  4. package/lib/commands/connector/sign.js +108 -12
  5. package/lib/commands/connector/sign.js.map +1 -1
  6. package/lib/commands/connector/validate.js +110 -10
  7. package/lib/commands/connector/validate.js.map +1 -1
  8. package/lib/commands/migrator/convert.d.ts +3 -0
  9. package/lib/commands/migrator/convert.js +201 -20
  10. package/lib/commands/migrator/convert.js.map +1 -1
  11. package/lib/templates/migrator-runners/plugin_override.txt +76 -4
  12. package/lib/templates/migrator-runners/runner_override.txt +30 -0
  13. package/lib/templates/migrator-runners/script_override.txt +77 -5
  14. package/lib/templates/swimlane/__init__.py +18 -0
  15. package/lib/templates/swimlane/core/__init__.py +0 -0
  16. package/lib/templates/swimlane/core/adapters/__init__.py +10 -0
  17. package/lib/templates/swimlane/core/adapters/app.py +59 -0
  18. package/lib/templates/swimlane/core/adapters/app_revision.py +49 -0
  19. package/lib/templates/swimlane/core/adapters/helper.py +84 -0
  20. package/lib/templates/swimlane/core/adapters/record.py +468 -0
  21. package/lib/templates/swimlane/core/adapters/record_revision.py +43 -0
  22. package/lib/templates/swimlane/core/adapters/report.py +65 -0
  23. package/lib/templates/swimlane/core/adapters/task.py +58 -0
  24. package/lib/templates/swimlane/core/adapters/usergroup.py +183 -0
  25. package/lib/templates/swimlane/core/bulk.py +48 -0
  26. package/lib/templates/swimlane/core/cache.py +165 -0
  27. package/lib/templates/swimlane/core/client.py +466 -0
  28. package/lib/templates/swimlane/core/cursor.py +100 -0
  29. package/lib/templates/swimlane/core/fields/__init__.py +46 -0
  30. package/lib/templates/swimlane/core/fields/attachment.py +82 -0
  31. package/lib/templates/swimlane/core/fields/base/__init__.py +15 -0
  32. package/lib/templates/swimlane/core/fields/base/cursor.py +90 -0
  33. package/lib/templates/swimlane/core/fields/base/field.py +149 -0
  34. package/lib/templates/swimlane/core/fields/base/multiselect.py +116 -0
  35. package/lib/templates/swimlane/core/fields/comment.py +48 -0
  36. package/lib/templates/swimlane/core/fields/datetime.py +112 -0
  37. package/lib/templates/swimlane/core/fields/history.py +28 -0
  38. package/lib/templates/swimlane/core/fields/list.py +266 -0
  39. package/lib/templates/swimlane/core/fields/number.py +38 -0
  40. package/lib/templates/swimlane/core/fields/reference.py +169 -0
  41. package/lib/templates/swimlane/core/fields/text.py +30 -0
  42. package/lib/templates/swimlane/core/fields/tracking.py +10 -0
  43. package/lib/templates/swimlane/core/fields/usergroup.py +137 -0
  44. package/lib/templates/swimlane/core/fields/valueslist.py +70 -0
  45. package/lib/templates/swimlane/core/resolver.py +46 -0
  46. package/lib/templates/swimlane/core/resources/__init__.py +0 -0
  47. package/lib/templates/swimlane/core/resources/app.py +136 -0
  48. package/lib/templates/swimlane/core/resources/app_revision.py +43 -0
  49. package/lib/templates/swimlane/core/resources/attachment.py +64 -0
  50. package/lib/templates/swimlane/core/resources/base.py +55 -0
  51. package/lib/templates/swimlane/core/resources/comment.py +33 -0
  52. package/lib/templates/swimlane/core/resources/record.py +499 -0
  53. package/lib/templates/swimlane/core/resources/record_revision.py +44 -0
  54. package/lib/templates/swimlane/core/resources/report.py +259 -0
  55. package/lib/templates/swimlane/core/resources/revision_base.py +69 -0
  56. package/lib/templates/swimlane/core/resources/task.py +16 -0
  57. package/lib/templates/swimlane/core/resources/usergroup.py +166 -0
  58. package/lib/templates/swimlane/core/search.py +31 -0
  59. package/lib/templates/swimlane/core/wrappedsession.py +12 -0
  60. package/lib/templates/swimlane/exceptions.py +191 -0
  61. package/lib/templates/swimlane/utils/__init__.py +132 -0
  62. package/lib/templates/swimlane/utils/date_validator.py +4 -0
  63. package/lib/templates/swimlane/utils/list_validator.py +7 -0
  64. package/lib/templates/swimlane/utils/str_validator.py +10 -0
  65. package/lib/templates/swimlane/utils/version.py +101 -0
  66. package/lib/transformers/base-transformer.js +61 -14
  67. package/lib/transformers/base-transformer.js.map +1 -1
  68. package/lib/transformers/connector-generator.d.ts +104 -2
  69. package/lib/transformers/connector-generator.js +1234 -51
  70. package/lib/transformers/connector-generator.js.map +1 -1
  71. package/lib/types/migrator-types.d.ts +22 -0
  72. package/lib/types/migrator-types.js.map +1 -1
  73. package/oclif.manifest.json +1 -1
  74. package/package.json +6 -6
@@ -0,0 +1,468 @@
1
+ import six
2
+
3
+ from swimlane.core.bulk import Replace, _BulkModificationOperation
4
+ from swimlane.core.cache import check_cache
5
+ from swimlane.core.resolver import AppResolver
6
+ from swimlane.core.resources.record import Record, record_factory
7
+ from swimlane.core.resources.report import Report
8
+ from swimlane.utils import random_string, one_of_keyword_only, validate_type
9
+ from swimlane.utils.version import requires_swimlane_version
10
+
11
+ class RecordAdapter(AppResolver):
12
+ """Handles retrieval and creation of Swimlane Record resources"""
13
+
14
+ @check_cache(Record)
15
+ @one_of_keyword_only('id', 'tracking_id')
16
+ def get(self, key, value):
17
+ """Get a single record by id
18
+
19
+ Supports resource cache
20
+
21
+ .. versionchanged:: 2.17.0
22
+ Added option to retrieve record by tracking_id
23
+
24
+ Keyword Args:
25
+ id (str): Full record ID
26
+ tracking_id (str): Record Tracking ID
27
+
28
+ Returns:
29
+ Record: Matching Record instance returned from API
30
+
31
+ Raises:
32
+ TypeError: No id argument provided
33
+ ValueError: The lookup value is empty or None
34
+ """
35
+
36
+ if not value:
37
+ raise ValueError('The value provided for the key "{0}" cannot be empty or None'.format(key))
38
+
39
+ if key == 'id':
40
+ response = self._swimlane.request('get', "app/{0}/record/{1}".format(self._app.id, value))
41
+ return Record(self._app, response.json())
42
+ if key == 'tracking_id':
43
+ response = self._swimlane.request('get', "app/{0}/record/tracking/{1}".format(self._app.id, value))
44
+ return Record(self._app, response.json())
45
+
46
+ def search(self, *filters, filter_type = 'And', **kwargs):
47
+ """Shortcut to generate a new temporary search report using provided filters and return the resulting records
48
+
49
+ Args:
50
+ *filters (tuple): Zero or more filter tuples of (field_name, operator, field_value)
51
+
52
+ Keyword Args:
53
+ keywords (list(str)): List of strings of keywords to use in report search
54
+ limit (int): Set maximum number of returned Records, defaults to `Report.default_limit`. Set to 0 to return
55
+ all records
56
+ page_size: Set maximum number of returned Records per page, defaults to 1000.
57
+ Set to 0 to return all records
58
+ sort: Tuple of (field_name, order) by which results will be sorted
59
+ columns (list(str)): List of strings of field names to populate in the resulting records. Defaults to all
60
+ available fields
61
+
62
+ Notes:
63
+ Uses a temporary Report instance with a random name to facilitate search. Records are normally paginated,
64
+ but are returned as a single list here, potentially causing performance issues with large searches.
65
+
66
+ All provided filters are AND'ed together
67
+
68
+ Filter operators and sort orders are available as constants in `swimlane.core.search`
69
+
70
+ Examples:
71
+
72
+ ::
73
+
74
+ # Return records matching all filters with default limit and page size
75
+
76
+ from swimlane.core import search
77
+
78
+ records = app.records.search(
79
+ ('field_name', 'equals', 'field_value'),
80
+ ('other_field', search.NOT_EQ, 'value')
81
+ )
82
+
83
+ ::
84
+
85
+ # Run keyword search with multiple keywords
86
+ records = app.records.search(keywords=['example', 'test'])
87
+
88
+ ::
89
+
90
+ # Return all records from app
91
+ records = app.records.search(limit=0)
92
+
93
+ ::
94
+
95
+ # Populate only the specified field and sort results
96
+ records = app.records.search(columns=['field_name'], sort=('field_name', 'ascending'))
97
+
98
+
99
+ Returns:
100
+ :class:`list` of :class:`~swimlane.core.resources.record.Record`: List of Record instances returned from the
101
+ search results
102
+ """
103
+ report = self._app.reports.build(
104
+ 'search-' + random_string(8),
105
+ keywords=kwargs.pop('keywords', []),
106
+ limit=kwargs.pop('limit', Report.default_limit),
107
+ page_size=kwargs.pop('page_size', 1000),
108
+ page_start=kwargs.pop('page_start', None),
109
+ page_end=kwargs.pop('page_end', None)
110
+ )
111
+
112
+ for filter_tuples in filters:
113
+ report.filter(*filter_tuples)
114
+
115
+ sort_tuple = kwargs.pop('sort', None)
116
+ if sort_tuple:
117
+ report.sort(*sort_tuple)
118
+
119
+ columns = kwargs.pop('columns', None)
120
+ if columns:
121
+ report.set_columns(*columns)
122
+
123
+ report.filter_type(filter_type)
124
+
125
+ return list(report)
126
+
127
+
128
+ def create(self, **fields):
129
+ """Create and return a new record in associated app and return the newly created Record instance
130
+
131
+ Args:
132
+ **fields: Field names and values to be validated and sent to server with create request
133
+
134
+ Notes:
135
+ Keyword arguments should be field names with their respective python values
136
+
137
+ Field values are validated before sending create request to server
138
+
139
+ Examples:
140
+ Create a new record on an app with simple field names
141
+
142
+ ::
143
+
144
+ record = app.records.create(
145
+ field_a='Some Value',
146
+ someOtherField=100,
147
+ ...
148
+ )
149
+
150
+ Create a new record on an app with complex field names
151
+
152
+ ::
153
+
154
+ record = app.records.create(**{
155
+ 'Field 1': 'Field 1 Value',
156
+ 'Field 2': 100,
157
+ ...
158
+ })
159
+
160
+ Returns:
161
+ Record: Newly created Record instance with data as returned from API response
162
+
163
+ Raises:
164
+ swimlane.exceptions.UnknownField: If any fields are provided that are not available on target app
165
+ swimlane.exceptions.ValidationError: If any field fails validation before creation
166
+ """
167
+ new_record = record_factory(self._app, fields)
168
+
169
+ new_record.save()
170
+
171
+ return new_record
172
+
173
+ @requires_swimlane_version('2.15')
174
+ def bulk_create(self, *records):
175
+ """Create and validate multiple records in associated app
176
+
177
+ Args:
178
+ *records (dict): One or more dicts of new record field names and values
179
+
180
+ Notes:
181
+ Requires Swimlane 2.15+
182
+
183
+ Validates like ``create``, but only sends a single request to create all provided fields, and does not
184
+ return the newly created records
185
+
186
+ Any validation failures on any of the records will abort the batch creation, not creating any new records
187
+
188
+ Does not return the newly created records
189
+
190
+ Examples:
191
+ Create 3 new records with single request
192
+
193
+ ::
194
+
195
+ app.records.bulk_create(
196
+ {'Field 1': 'value 1', ...},
197
+ {'Field 1': 'value 2', ...},
198
+ {'Field 1': 'value 3', ...}
199
+ )
200
+
201
+ Raises:
202
+ swimlane.exceptions.UnknownField: If any field in any new record cannot be found
203
+ swimlane.exceptions.ValidationError: If any field in any new record fails validation
204
+ TypeError: If no dict of fields was provided, or any provided argument is not a dict
205
+ """
206
+
207
+ if not records:
208
+ raise TypeError('Must provide at least one record')
209
+
210
+ if any(not isinstance(r, dict) for r in records):
211
+ raise TypeError('New records must be provided as dicts')
212
+
213
+ # Create local records from factory for initial full validation
214
+ new_records = []
215
+
216
+ for record_data in records:
217
+ record = record_factory(self._app, record_data)
218
+ record.validate()
219
+
220
+ new_records.append(record)
221
+
222
+ return self._swimlane.request(
223
+ 'post',
224
+ 'app/{}/record/batch'.format(self._app.id),
225
+ json=[r._raw for r in new_records]
226
+ ).json()
227
+
228
+ # pylint: disable=too-many-branches
229
+ @requires_swimlane_version('2.17')
230
+ def bulk_modify(self, *filters_or_records_or_ids, **kwargs):
231
+ """Shortcut to bulk modify records
232
+
233
+ .. versionadded:: 2.17.0
234
+
235
+ Args:
236
+ *filters_or_records_or_ids (tuple), (Record), or (string): a list of Records, a list of recordIds, a list of filters, or a list of both records and recordIds.
237
+
238
+ Keyword Args:
239
+ values (dict): Dictionary of one or more 'field_name': 'new_value' pairs to update
240
+
241
+ Notes:
242
+ Requires Swimlane 2.17+
243
+
244
+ Examples:
245
+
246
+ ::
247
+
248
+ # Bulk update records by filter
249
+
250
+ app.records.bulk_modify(
251
+ # Query filters
252
+ ('Field_1', 'equals', value1),
253
+ ('Field_2', 'equals', value2),
254
+ ...
255
+ # New values for records
256
+ values={
257
+ "Field_3": value3,
258
+ "Field_4": value4,
259
+ ...
260
+ }
261
+ )
262
+
263
+ # Bulk update records
264
+
265
+ record1 = app.records.get(tracking_id='APP-1')
266
+ record2 = app.records.get(tracking_id='APP-2')
267
+ record3 = app.records.get(tracking_id='APP-3')
268
+
269
+ app.records.bulk_modify(record1, record2, record3, values={"Field_Name": 'new value'})
270
+
271
+ # Using recordIds
272
+ app.records.bulk_modify("adtDzpdDRv9zM8C4o", "aHlAFdBBjE020Jrzb", "aAR67lIcEnLknaURw", values={"Field Name": "New Value"})
273
+
274
+ # Bulk modify by mixing record instances and record ids
275
+ record1 = app.records.get(tracking_id='APP-1')
276
+ app.records.bulk_modify(record1, "aHlAFdBBjE020Jrzb", "aAR67lIcEnLknaURw", values={"Field Name": "New Value"})
277
+
278
+ Returns:
279
+ :class:`string`: Bulk Modify Job ID
280
+ """
281
+ values = kwargs.pop('values', None)
282
+
283
+ if kwargs:
284
+ raise ValueError('Unexpected arguments: {}'.format(kwargs))
285
+
286
+ if not values:
287
+ raise ValueError('Must provide "values" as keyword argument')
288
+
289
+ if not isinstance(values, dict):
290
+ raise ValueError('values parameter must be dict of {"field_name": "update_value"} pairs')
291
+
292
+ _type = validate_filters_or_records_or_ids(filters_or_records_or_ids)
293
+
294
+ request_payload = {}
295
+ record_stub = record_factory(self._app)
296
+
297
+ if _type is Record or _type is str:
298
+ the_record_ids = []
299
+ for record_or_id in filters_or_records_or_ids:
300
+ if isinstance(record_or_id, Record):
301
+ the_record_ids.append(record_or_id.id)
302
+ else:
303
+ the_record_ids.append(record_or_id)
304
+
305
+ request_payload["recordIds"] = [item for item in the_record_ids]
306
+
307
+ # build filters
308
+ else:
309
+ filters = []
310
+ for filter_tuples in filters_or_records_or_ids:
311
+ field_name = record_stub.get_field(filter_tuples[0])
312
+
313
+ value = filter_tuples[2]
314
+ validate_type(field_name, value)
315
+
316
+ filters.append({
317
+ "fieldId": field_name.id,
318
+ "filterType": filter_tuples[1],
319
+ "value": field_name.get_report(filter_tuples[2])
320
+ })
321
+ request_payload['filters'] = filters
322
+
323
+ # Ensure all values are wrapped in a bulk modification operation, defaulting to Replace if not provided for
324
+ # backwards compatibility
325
+ for field_name in list(values.keys()):
326
+ modification_operation = values[field_name]
327
+ if not isinstance(modification_operation, _BulkModificationOperation):
328
+ values[field_name] = Replace(modification_operation)
329
+
330
+ # build modifications
331
+ modifications = []
332
+ for field_name, modification_operation in values.items():
333
+ # Lookup target field
334
+ modification_field = record_stub.get_field(field_name)
335
+ if not modification_field.bulk_modify_support:
336
+ raise ValueError('Field "{}" of Type "{}", is not supported for bulk modify'.format(
337
+ field_name,
338
+ modification_field.__class__.__name__
339
+ ))
340
+
341
+ modifications.append({
342
+ "fieldId": {
343
+ "value": modification_field.id,
344
+ "type": "id"
345
+ },
346
+ "value": modification_field.get_bulk_modify(modification_operation.value),
347
+ "type": modification_operation.type
348
+ })
349
+ request_payload['modifications'] = modifications
350
+ response = self._swimlane.request('put', "app/{0}/record/batch".format(self._app.id), json=request_payload)
351
+
352
+ # Update records if instances were used to submit bulk modify request after request was successful
353
+ if _type is Record or _type is str:
354
+ for record in filters_or_records_or_ids:
355
+ # if value is an ID, get the record instance
356
+ if type(record) is str:
357
+ record = self.get(id=record)
358
+ for field_name, modification_operation in six.iteritems(values):
359
+ record[field_name] = modification_operation.value
360
+
361
+ return response.text
362
+
363
+
364
+
365
+
366
+
367
+
368
+ @requires_swimlane_version('2.17')
369
+ def bulk_delete(self, *filters_or_records_or_ids):
370
+ """Shortcut to bulk delete records
371
+
372
+ .. versionadded:: 2.17.0
373
+
374
+ Args:
375
+ *filters_or_records_or_ids (tuple) or (Record): Either a list of Records, a list of filters, a list of recordIds, or a list of both records and recordIds.
376
+
377
+ Notes:
378
+ Requires Swimlane 2.17+
379
+
380
+ Examples:
381
+
382
+ ::
383
+
384
+ # Bulk delete records by filter
385
+ app.records.bulk_delete(
386
+ ('Field_1', 'equals', value1),
387
+ ('Field_2', 'equals', value2)
388
+ )
389
+
390
+ # Bulk delete by record instances
391
+ record1 = app.records.get(tracking_id='APP-1')
392
+ record2 = app.records.get(tracking_id='APP-2')
393
+ record3 = app.records.get(tracking_id='APP-3')
394
+ app.records.bulk_delete(record1, record2, record3)
395
+
396
+ # Bulk delete by record ids
397
+ app.records.bulk_delete("adtDzpdDRv9zM8C4o", "aHlAFdBBjE020Jrzb", "aAR67lIcEnLknaURw", values={"Field Name": "New Value"})
398
+
399
+ # Bulk delete by mixing record instances and record ids
400
+ record1 = app.records.get(tracking_id='APP-1')
401
+ app.records.bulk_delete(record1, "aHlAFdBBjE020Jrzb", "aAR67lIcEnLknaURw", values={"Field Name": "New Value"})
402
+
403
+ Returns:
404
+ :class:`string`: Bulk Modify Job ID
405
+ """
406
+
407
+ _type = validate_filters_or_records_or_ids(filters_or_records_or_ids)
408
+ data_dict = {}
409
+
410
+ # build record_id list
411
+ if _type is Record or _type is str:
412
+ record_ids = []
413
+ for item in filters_or_records_or_ids:
414
+ if isinstance(item, Record):
415
+ record_ids.append(item.id)
416
+ else:
417
+ record_ids.append(item)
418
+ data_dict['recordIds'] = record_ids
419
+
420
+ # build filters
421
+ else:
422
+ filters = []
423
+ record_stub = record_factory(self._app)
424
+ for filter_tuples in filters_or_records_or_ids:
425
+ field = record_stub.get_field(filter_tuples[0])
426
+
427
+ value = filter_tuples[2]
428
+ validate_type(field, value)
429
+
430
+ filters.append({
431
+ "fieldId": field.id,
432
+ "filterType": filter_tuples[1],
433
+ "value": field.get_report(filter_tuples[2])
434
+ })
435
+ data_dict['filters'] = filters
436
+
437
+ return self._swimlane.request('DELETE', "app/{0}/record/batch".format(self._app.id), json=data_dict).text
438
+
439
+
440
+ def validate_filters_or_records_or_ids(filters_or_records_or_ids):
441
+ """Validation for filters_or_records variable from bulk_modify and bulk_delete"""
442
+ # If filters_or_records_or_ids is empty, fail
443
+ if not filters_or_records_or_ids:
444
+ raise ValueError('Must provide at least one filter tuples, Records, or list of Ids')
445
+
446
+ _types = [type(item) for item in filters_or_records_or_ids]
447
+
448
+ types_dict = {
449
+ "record": 0,
450
+ "str": 0,
451
+ "tuple": 0
452
+ }
453
+
454
+ for _type in _types:
455
+ if _type is tuple:
456
+ types_dict["tuple"] = types_dict["tuple"] + 1
457
+ elif _type is Record:
458
+ types_dict["record"] = types_dict["tuple"] + 1
459
+ elif _type is str:
460
+ types_dict["str"] = types_dict["tuple"] + 1
461
+ else:
462
+ raise ValueError('Expected filter tuple, Record, or string, received {0}'.format(_type))
463
+
464
+ if types_dict["tuple"] > 0 and (types_dict["record"] > 0 or types_dict["str"] > 0):
465
+ raise ValueError('Cannot mix filter tuples with records or ids')
466
+
467
+ # either all tuples or a mix of record and str, which is handled together
468
+ return _types[0]
@@ -0,0 +1,43 @@
1
+ import math
2
+
3
+ from swimlane.core.resolver import AppResolver
4
+ from swimlane.core.resources.record_revision import RecordRevision
5
+
6
+
7
+ class RecordRevisionAdapter(AppResolver):
8
+ """Handles retrieval of Swimlane Record Revision resources"""
9
+
10
+ def __init__(self, app, record):
11
+ super(RecordRevisionAdapter, self).__init__(app)
12
+ self.record = record
13
+
14
+ def get_all(self):
15
+ """Get all revisions for a single record.
16
+
17
+ Returns:
18
+ RecordRevision[]: All record revisions for the given record ID.
19
+ """
20
+ raw_revisions = self._swimlane.request('get',
21
+ 'app/{0}/record/{1}/history'.format(self._app.id, self.record.id)).json()
22
+ return [RecordRevision(self._app, raw) for raw in raw_revisions]
23
+
24
+ def get(self, revision_number):
25
+ """Gets a specific record revision.
26
+
27
+ Keyword Args:
28
+ revision_number (float): Record revision number
29
+
30
+ Returns:
31
+ RecordRevision: The RecordRevision for the given revision number.
32
+ Raises: When revision is not an integer, a float NOT ending in ".0", or is less than 1
33
+ """
34
+ if isinstance(revision_number, (int, float)):
35
+ if revision_number > 0 and revision_number % math.floor(revision_number) == 0:
36
+ record_revision_raw = self._swimlane.request('get',
37
+ 'app/{0}/record/{1}/history/{2}'.format(self._app.id,
38
+ self.record.id,
39
+ revision_number)).json()
40
+ return RecordRevision(self._app, record_revision_raw)
41
+
42
+
43
+ raise ValueError('The revision number must be a positive whole number greater than 0')
@@ -0,0 +1,65 @@
1
+ import weakref
2
+
3
+ from swimlane.core.resolver import SwimlaneResolver
4
+ from swimlane.core.resources.report import Report, report_factory
5
+ from swimlane.utils.str_validator import validate_str, validate_str_format
6
+
7
+ class ReportAdapter(SwimlaneResolver):
8
+ """Handles retrieval and creation of Report resources"""
9
+
10
+ def __init__(self, app):
11
+ super(ReportAdapter, self).__init__(app._swimlane)
12
+
13
+ self.__ref_app = weakref.ref(app)
14
+
15
+ @property
16
+ def _app(self):
17
+ """Resolve weak app reference"""
18
+ return self.__ref_app()
19
+
20
+ def list(self):
21
+ """Retrieve all reports for parent app
22
+
23
+ Returns:
24
+ :class:`list` of :class:`~swimlane.core.resources.report.Report`: List of all returned reports
25
+ """
26
+ raw_reports = self._swimlane.request('get', "reports/app/{}".format(self._app.id)).json()
27
+
28
+ return [Report(self._app, raw_report) for raw_report in raw_reports]
29
+
30
+ def get(self, report_id):
31
+ """Retrieve report by ID
32
+
33
+ Args:
34
+ report_id (str): Full report ID
35
+
36
+ Returns:
37
+ Report: Corresponding Report instance
38
+ """
39
+ validate_str(report_id, 'report_id')
40
+ validate_str_format(report_id, 'report_id')
41
+
42
+ return Report(
43
+ self._app,
44
+ self._swimlane.request('get', "reports/{0}".format(report_id)).json()
45
+ )
46
+
47
+ def build(self, name, **kwargs):
48
+ """Report instance factory for the adapter's App
49
+
50
+ Args:
51
+ name (str): New Report name
52
+
53
+ Keyword Args:
54
+ **kwargs: Extra keyword args passed to Report class
55
+
56
+ Returns:
57
+ Report: Newly created local Report instance
58
+ """
59
+ valid = lambda input: input is not None and isinstance(input, int) and input >= 0
60
+ limit = kwargs.get('limit', None)
61
+
62
+ if "limit" in kwargs and not valid(limit):
63
+ raise ValueError('The limit value must be a whole number of zero or above')
64
+
65
+ return report_factory(self._app, name, **kwargs)
@@ -0,0 +1,58 @@
1
+ from swimlane.core.resolver import SwimlaneResolver
2
+ from swimlane.core.cache import check_cache
3
+ from swimlane.core.resources.task import Task
4
+ from swimlane.utils import one_of_keyword_only
5
+
6
+
7
+ class TaskAdapter(SwimlaneResolver):
8
+ """Handles retreival of Swimlane Task Resources and execution of tasks against records."""
9
+
10
+ @check_cache(Task)
11
+ @one_of_keyword_only('id', 'name')
12
+ def get(self, key, value):
13
+ """Get a single task by id or name"""
14
+ if key == 'id':
15
+ response = self._swimlane.request('get', 'task/{id}'.format(id=value))
16
+ return Task(self._swimlane, response.json())
17
+
18
+ if key == 'name':
19
+ response = self._swimlane.request('get', 'task/light')
20
+ for task in response.json():
21
+ if value == task.get('name'):
22
+ return self._get_full(task.get('id'))
23
+ raise ValueError('No task with name "{value}"'.format(value=value))
24
+
25
+ def list(self):
26
+ """Retrieve list of all tasks.
27
+
28
+ Returns:
29
+ :class:`list` of :class:`~swimlane.core.resources.task.Task`: List of all tasks.
30
+ """
31
+ response = self._swimlane.request('get', 'task/light')
32
+ return [self._get_full(item.get('id')) for item in response.json()]
33
+
34
+ def execute(self, task_name, raw_record):
35
+ """Execute job by name.
36
+
37
+ Returns:
38
+ Response of request from the API endpoint.
39
+ """
40
+ task_id = self.get(name=task_name).id
41
+ data = {
42
+ 'taskId': task_id,
43
+ 'record': raw_record,
44
+ }
45
+ #return self._swimlane.request('post', 'task/execute/record', json=data)
46
+ if self._swimlane._execute_task_webhook_url:
47
+ return self._swimlane._session.post(self._swimlane._execute_task_webhook_url, json=data)
48
+ else:
49
+ raise Exception('Task webhook URL is not set')
50
+
51
+ def _get_full(self, task_id):
52
+ """Retreived complete task raw json of task from API.
53
+
54
+ Returns:
55
+ :class:`~swimlane.core.resources.task.Task`: Single Task object.
56
+ """
57
+ response = self._swimlane.request('get', 'task/{task_id}'.format(task_id=task_id))
58
+ return Task(self._swimlane, response.json())