@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.
- package/README.md +18 -18
- package/lib/commands/connector/build.js +168 -44
- package/lib/commands/connector/build.js.map +1 -1
- package/lib/commands/connector/sign.js +108 -12
- package/lib/commands/connector/sign.js.map +1 -1
- package/lib/commands/connector/validate.js +110 -10
- package/lib/commands/connector/validate.js.map +1 -1
- package/lib/commands/migrator/convert.d.ts +3 -0
- package/lib/commands/migrator/convert.js +201 -20
- package/lib/commands/migrator/convert.js.map +1 -1
- package/lib/templates/migrator-runners/plugin_override.txt +76 -4
- package/lib/templates/migrator-runners/runner_override.txt +30 -0
- package/lib/templates/migrator-runners/script_override.txt +77 -5
- package/lib/templates/swimlane/__init__.py +18 -0
- package/lib/templates/swimlane/core/__init__.py +0 -0
- package/lib/templates/swimlane/core/adapters/__init__.py +10 -0
- package/lib/templates/swimlane/core/adapters/app.py +59 -0
- package/lib/templates/swimlane/core/adapters/app_revision.py +49 -0
- package/lib/templates/swimlane/core/adapters/helper.py +84 -0
- package/lib/templates/swimlane/core/adapters/record.py +468 -0
- package/lib/templates/swimlane/core/adapters/record_revision.py +43 -0
- package/lib/templates/swimlane/core/adapters/report.py +65 -0
- package/lib/templates/swimlane/core/adapters/task.py +58 -0
- package/lib/templates/swimlane/core/adapters/usergroup.py +183 -0
- package/lib/templates/swimlane/core/bulk.py +48 -0
- package/lib/templates/swimlane/core/cache.py +165 -0
- package/lib/templates/swimlane/core/client.py +466 -0
- package/lib/templates/swimlane/core/cursor.py +100 -0
- package/lib/templates/swimlane/core/fields/__init__.py +46 -0
- package/lib/templates/swimlane/core/fields/attachment.py +82 -0
- package/lib/templates/swimlane/core/fields/base/__init__.py +15 -0
- package/lib/templates/swimlane/core/fields/base/cursor.py +90 -0
- package/lib/templates/swimlane/core/fields/base/field.py +149 -0
- package/lib/templates/swimlane/core/fields/base/multiselect.py +116 -0
- package/lib/templates/swimlane/core/fields/comment.py +48 -0
- package/lib/templates/swimlane/core/fields/datetime.py +112 -0
- package/lib/templates/swimlane/core/fields/history.py +28 -0
- package/lib/templates/swimlane/core/fields/list.py +266 -0
- package/lib/templates/swimlane/core/fields/number.py +38 -0
- package/lib/templates/swimlane/core/fields/reference.py +169 -0
- package/lib/templates/swimlane/core/fields/text.py +30 -0
- package/lib/templates/swimlane/core/fields/tracking.py +10 -0
- package/lib/templates/swimlane/core/fields/usergroup.py +137 -0
- package/lib/templates/swimlane/core/fields/valueslist.py +70 -0
- package/lib/templates/swimlane/core/resolver.py +46 -0
- package/lib/templates/swimlane/core/resources/__init__.py +0 -0
- package/lib/templates/swimlane/core/resources/app.py +136 -0
- package/lib/templates/swimlane/core/resources/app_revision.py +43 -0
- package/lib/templates/swimlane/core/resources/attachment.py +64 -0
- package/lib/templates/swimlane/core/resources/base.py +55 -0
- package/lib/templates/swimlane/core/resources/comment.py +33 -0
- package/lib/templates/swimlane/core/resources/record.py +499 -0
- package/lib/templates/swimlane/core/resources/record_revision.py +44 -0
- package/lib/templates/swimlane/core/resources/report.py +259 -0
- package/lib/templates/swimlane/core/resources/revision_base.py +69 -0
- package/lib/templates/swimlane/core/resources/task.py +16 -0
- package/lib/templates/swimlane/core/resources/usergroup.py +166 -0
- package/lib/templates/swimlane/core/search.py +31 -0
- package/lib/templates/swimlane/core/wrappedsession.py +12 -0
- package/lib/templates/swimlane/exceptions.py +191 -0
- package/lib/templates/swimlane/utils/__init__.py +132 -0
- package/lib/templates/swimlane/utils/date_validator.py +4 -0
- package/lib/templates/swimlane/utils/list_validator.py +7 -0
- package/lib/templates/swimlane/utils/str_validator.py +10 -0
- package/lib/templates/swimlane/utils/version.py +101 -0
- package/lib/transformers/base-transformer.js +61 -14
- package/lib/transformers/base-transformer.js.map +1 -1
- package/lib/transformers/connector-generator.d.ts +104 -2
- package/lib/transformers/connector-generator.js +1234 -51
- package/lib/transformers/connector-generator.js.map +1 -1
- package/lib/types/migrator-types.d.ts +22 -0
- package/lib/types/migrator-types.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import pendulum
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from swimlane.core.cursor import PaginatedCursor
|
|
6
|
+
from swimlane.core.fields.list import ListField
|
|
7
|
+
from swimlane.core.resources.base import APIResource
|
|
8
|
+
from swimlane.core.resources.record import Record, record_factory
|
|
9
|
+
from swimlane.core.search import CONTAINS, EQ, EXCLUDES, NOT_EQ, LT, GT, LTE, GTE, ASC, DESC
|
|
10
|
+
from swimlane.utils import validate_type
|
|
11
|
+
|
|
12
|
+
ALLOWED_OPERATORS = ['Or', 'And']
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Report(APIResource, PaginatedCursor):
|
|
16
|
+
"""A report class used for searching
|
|
17
|
+
|
|
18
|
+
Can be iterated over to retrieve results
|
|
19
|
+
|
|
20
|
+
Notes:
|
|
21
|
+
Record retrieval is lazily evaluated and cached internally, adding a filter and attempting to iterate again will
|
|
22
|
+
not respect the additional filter and will return the same set of records each time
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
|
|
26
|
+
Lazy retrieval of records with direct iteration over report
|
|
27
|
+
|
|
28
|
+
::
|
|
29
|
+
|
|
30
|
+
report = app.reports.build('new-report')
|
|
31
|
+
report.filter('field_1', 'equals', 'value')
|
|
32
|
+
|
|
33
|
+
for record in report:
|
|
34
|
+
do_thing(record)
|
|
35
|
+
|
|
36
|
+
Full immediate retrieval of all records
|
|
37
|
+
|
|
38
|
+
::
|
|
39
|
+
|
|
40
|
+
report = app.reports.build('new-report')
|
|
41
|
+
report.filter('field_1', 'doesNotEqual', 'value')
|
|
42
|
+
|
|
43
|
+
records = list(report)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
name (str): Report name
|
|
48
|
+
|
|
49
|
+
Keyword Args:
|
|
50
|
+
limit (int): Max number of records to return from report/search
|
|
51
|
+
page_size (int): Max number of records per page
|
|
52
|
+
keywords (list(str)): List of keywords to use in report/search
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
_type = "Core.Models.Search.StatsReport, Core"
|
|
56
|
+
|
|
57
|
+
_FILTER_OPERANDS = (
|
|
58
|
+
EQ,
|
|
59
|
+
NOT_EQ,
|
|
60
|
+
CONTAINS,
|
|
61
|
+
EXCLUDES,
|
|
62
|
+
LT,
|
|
63
|
+
GT,
|
|
64
|
+
LTE,
|
|
65
|
+
GTE
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
_SORT_ORDERS = (
|
|
69
|
+
ASC,
|
|
70
|
+
DESC
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
default_limit = 50
|
|
74
|
+
default_page_start = None
|
|
75
|
+
default_page_end = None
|
|
76
|
+
|
|
77
|
+
def __init__(self, app, raw, **kwargs):
|
|
78
|
+
APIResource.__init__(self, app._swimlane, raw)
|
|
79
|
+
PaginatedCursor.__init__(self,
|
|
80
|
+
limit=kwargs.pop('limit', self.default_limit),
|
|
81
|
+
page_size=kwargs.pop('page_size', self.default_page_size),
|
|
82
|
+
page_start=kwargs.pop('page_start', self.default_page_start),
|
|
83
|
+
page_end=kwargs.pop('page_end', self.default_page_end)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
self.name = self._raw['name']
|
|
87
|
+
self.keywords = kwargs.pop('keywords', [])
|
|
88
|
+
|
|
89
|
+
self._app = app
|
|
90
|
+
|
|
91
|
+
for field_id in self._app._fields_by_id.keys():
|
|
92
|
+
self._raw['columns'].append(field_id)
|
|
93
|
+
|
|
94
|
+
def filter_type(self, filter_type):
|
|
95
|
+
filter_type = filter_type.capitalize()
|
|
96
|
+
self.validateOperator(filter_type)
|
|
97
|
+
self.filter_type = filter_type
|
|
98
|
+
|
|
99
|
+
def validateOperator(self, operator):
|
|
100
|
+
if operator not in ALLOWED_OPERATORS:
|
|
101
|
+
raise ValueError('filter_type value not allowed')
|
|
102
|
+
|
|
103
|
+
def __str__(self):
|
|
104
|
+
return self.name
|
|
105
|
+
|
|
106
|
+
def _retrieve_raw_elements(self, page):
|
|
107
|
+
body = self._raw.copy()
|
|
108
|
+
|
|
109
|
+
body['pageSize'] = self.page_size
|
|
110
|
+
body['offset'] = page
|
|
111
|
+
if(type(self.filter_type) is str):
|
|
112
|
+
body['filterType'] = self.filter_type
|
|
113
|
+
body['keywords'] = ', '.join(self.keywords)
|
|
114
|
+
|
|
115
|
+
response = self._swimlane.request('post', 'search', json=body)
|
|
116
|
+
return response.json()['results'].get(self._app.id, [])
|
|
117
|
+
|
|
118
|
+
def _parse_raw_element(self, raw_element):
|
|
119
|
+
return Record(self._app, raw_element)
|
|
120
|
+
|
|
121
|
+
def filter(self, field_name, operand, value):
|
|
122
|
+
"""Adds a filter to report
|
|
123
|
+
|
|
124
|
+
Notes:
|
|
125
|
+
1. All filters are currently AND'ed together.
|
|
126
|
+
2. None values work like a wildcard and will skip type verification.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
field_name (str): Target field name to filter on
|
|
130
|
+
operand (str): Operand used in comparison. See `swimlane.core.search` for options
|
|
131
|
+
value: Target value used in comparison
|
|
132
|
+
"""
|
|
133
|
+
if operand not in self._FILTER_OPERANDS:
|
|
134
|
+
raise ValueError('Operand must be one of {}'.format(', '.join(self._FILTER_OPERANDS)))
|
|
135
|
+
|
|
136
|
+
field = self._get_stub_field(field_name)
|
|
137
|
+
|
|
138
|
+
validate_type(field, value)
|
|
139
|
+
|
|
140
|
+
value = self.parse_field_value(field, value)
|
|
141
|
+
|
|
142
|
+
self._raw['filters'].append({
|
|
143
|
+
"fieldId": field.id,
|
|
144
|
+
"filterType": operand,
|
|
145
|
+
"value": field.get_report(value)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
def sort(self, field_name, order):
|
|
149
|
+
"""Adds a sort to report
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
field_name (str): Target field name to sort by
|
|
153
|
+
order (str): Sort order
|
|
154
|
+
"""
|
|
155
|
+
if (order not in self._SORT_ORDERS):
|
|
156
|
+
raise ValueError('Order must be one of {}'.format(', '.join(self._SORT_ORDERS)))
|
|
157
|
+
|
|
158
|
+
field = self._get_stub_field(field_name)
|
|
159
|
+
|
|
160
|
+
self._raw['sorts'][field.id] = order
|
|
161
|
+
|
|
162
|
+
def set_columns(self, *field_names):
|
|
163
|
+
"""Set specified columns for report
|
|
164
|
+
|
|
165
|
+
Notes:
|
|
166
|
+
The Tracking Id column is always included
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
*field_names (str): Zero or more column names
|
|
170
|
+
"""
|
|
171
|
+
self._raw['columns'] = []
|
|
172
|
+
for field_name in field_names:
|
|
173
|
+
field = self._get_stub_field(field_name)
|
|
174
|
+
|
|
175
|
+
self._raw['columns'].append(field.id)
|
|
176
|
+
|
|
177
|
+
if self._app.tracking_id not in self._raw['columns']:
|
|
178
|
+
self._raw['columns'].append(self._app.tracking_id)
|
|
179
|
+
|
|
180
|
+
def _get_stub_field(self, field_name):
|
|
181
|
+
if not field_name or not isinstance(field_name, str):
|
|
182
|
+
raise ValueError('field_name is of an invalid format, expected non-empty string')
|
|
183
|
+
|
|
184
|
+
# Use temp Record instance for target app to translate values into expected API format
|
|
185
|
+
record_stub = record_factory(self._app)
|
|
186
|
+
return record_stub.get_field(field_name)
|
|
187
|
+
|
|
188
|
+
def parse_field_value(self, field, value):
|
|
189
|
+
if isinstance(field, ListField):
|
|
190
|
+
type = self.get_field_list_type(field.input_type)
|
|
191
|
+
value = self.get_default_value(value, field.input_type)
|
|
192
|
+
if isinstance(field, ListField) and not isinstance(value, list) and value is not None:
|
|
193
|
+
self.validate_type(value, type, field.input_type)
|
|
194
|
+
return [value]
|
|
195
|
+
elif isinstance(field, ListField) and isinstance(value, list) and any(not isinstance(elem, type) for elem in value):
|
|
196
|
+
raise TypeError('Field item must be a {}.'.format(field.input_type))
|
|
197
|
+
return value
|
|
198
|
+
|
|
199
|
+
def validate_type(self, value, type, type_name=None):
|
|
200
|
+
if(not type_name):
|
|
201
|
+
type_name = type
|
|
202
|
+
if not isinstance(value, type):
|
|
203
|
+
raise TypeError('Field must be a {}.'.format(type_name))
|
|
204
|
+
|
|
205
|
+
def get_default_value(self, value, field_type):
|
|
206
|
+
if(value == '' and field_type == 'text'):
|
|
207
|
+
value = None
|
|
208
|
+
return value
|
|
209
|
+
|
|
210
|
+
def get_field_list_type(self, field_type):
|
|
211
|
+
if field_type == 'text':
|
|
212
|
+
return str
|
|
213
|
+
elif field_type == 'numeric':
|
|
214
|
+
return (int, float)
|
|
215
|
+
|
|
216
|
+
def report_factory(app, report_name, **kwargs):
|
|
217
|
+
"""Report instance factory populating boilerplate raw data
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
app (App): Swimlane App instance
|
|
221
|
+
report_name (str): Generated Report name
|
|
222
|
+
|
|
223
|
+
Keyword Args
|
|
224
|
+
**kwargs: Kwargs to pass to the Report class
|
|
225
|
+
"""
|
|
226
|
+
# pylint: disable=protected-access
|
|
227
|
+
created = pendulum.now().to_rfc3339_string()
|
|
228
|
+
user_model = app._swimlane.user.as_usergroup_selection()
|
|
229
|
+
|
|
230
|
+
return Report(
|
|
231
|
+
app,
|
|
232
|
+
{
|
|
233
|
+
"$type": Report._type,
|
|
234
|
+
"groupBys": [],
|
|
235
|
+
"aggregates": [],
|
|
236
|
+
"applicationIds": [app.id],
|
|
237
|
+
"columns": [],
|
|
238
|
+
"sorts": {
|
|
239
|
+
"$type": "System.Collections.Generic.Dictionary`2"
|
|
240
|
+
"[[System.String, mscorlib],"
|
|
241
|
+
"[Core.Models.Search.SortTypes, Core]], mscorlib",
|
|
242
|
+
},
|
|
243
|
+
"filters": [],
|
|
244
|
+
"defaultSearchReport": False,
|
|
245
|
+
"allowed": [],
|
|
246
|
+
"permissions": {
|
|
247
|
+
"$type": "Core.Models.Security.PermissionMatrix, Core"
|
|
248
|
+
},
|
|
249
|
+
"createdDate": created,
|
|
250
|
+
"modifiedDate": created,
|
|
251
|
+
"createdByUser": user_model,
|
|
252
|
+
"modifiedByUser": user_model,
|
|
253
|
+
"id": None,
|
|
254
|
+
"name": report_name,
|
|
255
|
+
"disabled": False,
|
|
256
|
+
"keywords": ""
|
|
257
|
+
},
|
|
258
|
+
**kwargs
|
|
259
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import pendulum
|
|
2
|
+
|
|
3
|
+
from swimlane.core.resources.base import APIResource
|
|
4
|
+
from swimlane.core.resources.usergroup import UserGroup
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RevisionBase(APIResource):
|
|
8
|
+
"""
|
|
9
|
+
The base class representing a single revision returned from a History lookup.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
Attributes:
|
|
13
|
+
modified_date: The date this revision was created.
|
|
14
|
+
revision_number: The revision number of this revision.
|
|
15
|
+
status: Indicates whether this revision is the current revision or a historical revision.
|
|
16
|
+
user: The user that saved this revision.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, swimlane, raw):
|
|
20
|
+
super(RevisionBase, self).__init__(swimlane, raw)
|
|
21
|
+
|
|
22
|
+
self._modified_date = pendulum.parse(self._raw['modifiedDate'])
|
|
23
|
+
self._revision_number = self._raw['revisionNumber']
|
|
24
|
+
self.status = self._raw['status']
|
|
25
|
+
|
|
26
|
+
# UserGroupSelection, can't set as User without additional lookup
|
|
27
|
+
self._user = UserGroup(self._swimlane, self._raw['userId'])
|
|
28
|
+
|
|
29
|
+
self._raw_version = self._raw['version']
|
|
30
|
+
self._version = None
|
|
31
|
+
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return '{} ({})'.format(self.version, self.revision_number)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def version(self):
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
|
|
39
|
+
def for_json(self):
|
|
40
|
+
"""Return revision metadata"""
|
|
41
|
+
return {
|
|
42
|
+
'modifiedDate': self._raw['modifiedDate'],
|
|
43
|
+
'revisionNumber': self.revision_number,
|
|
44
|
+
'user': self.user.for_json()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def revision_number(self):
|
|
49
|
+
return self._revision_number
|
|
50
|
+
|
|
51
|
+
@revision_number.setter
|
|
52
|
+
def revision_number(self, value):
|
|
53
|
+
raise AttributeError("can't set attribute")
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def modified_date(self):
|
|
57
|
+
return self._modified_date
|
|
58
|
+
|
|
59
|
+
@modified_date.setter
|
|
60
|
+
def modified_date(self, value):
|
|
61
|
+
raise AttributeError("can't set attribute")
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def user(self):
|
|
65
|
+
return self._user
|
|
66
|
+
|
|
67
|
+
@user.setter
|
|
68
|
+
def user(self, value):
|
|
69
|
+
raise AttributeError("can't set attribute")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from swimlane.core.resources.base import APIResource
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Task(APIResource):
|
|
5
|
+
_type = 'Core.Models.Integrations.Task, Core'
|
|
6
|
+
|
|
7
|
+
def __init__(self, swimlane, raw):
|
|
8
|
+
super(Task, self).__init__(swimlane, raw)
|
|
9
|
+
self.app_id = raw.get('applicationId')
|
|
10
|
+
self.id = raw.get('id')
|
|
11
|
+
self.name = raw.get('name')
|
|
12
|
+
self.script = raw.get('action').get('script')
|
|
13
|
+
|
|
14
|
+
def __str__(self):
|
|
15
|
+
return '{self.name}'.format(self=self)
|
|
16
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from functools import total_ordering
|
|
2
|
+
|
|
3
|
+
from swimlane.core.cursor import Cursor
|
|
4
|
+
from swimlane.core.resolver import SwimlaneResolver
|
|
5
|
+
from swimlane.core.resources.base import APIResource
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# pylint: disable=abstract-method
|
|
9
|
+
@total_ordering
|
|
10
|
+
class UserGroup(APIResource):
|
|
11
|
+
"""Base class for Users and Groups
|
|
12
|
+
|
|
13
|
+
Notes:
|
|
14
|
+
Returned in some places where determining whether object is a User or Group is not possible without additional
|
|
15
|
+
requests. Use appropriate adapter on `swimlane` client to retrieve more specific instance using `id` as needed
|
|
16
|
+
|
|
17
|
+
Can be compared to User or Group instances directly without ensuring the classes are the same
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
id (str): Full user/group ID
|
|
21
|
+
name (str): User/group name
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, swimlane, raw):
|
|
25
|
+
super(UserGroup, self).__init__(swimlane, raw)
|
|
26
|
+
|
|
27
|
+
self.id = self._raw['id']
|
|
28
|
+
self.name = self._raw['name']
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
return self.name
|
|
32
|
+
|
|
33
|
+
def __hash__(self):
|
|
34
|
+
return hash((self.id, self.name))
|
|
35
|
+
|
|
36
|
+
def __eq__(self, other):
|
|
37
|
+
"""Override to allow equality comparisons across UserGroup, User, and Group instances"""
|
|
38
|
+
return isinstance(other, UserGroup) and hash(self) == hash(other)
|
|
39
|
+
|
|
40
|
+
def __lt__(self, other):
|
|
41
|
+
if not isinstance(other, UserGroup):
|
|
42
|
+
raise TypeError('Comparisons not supported between instances of "{}" and "{}"'.format(
|
|
43
|
+
other.__class__.__name__,
|
|
44
|
+
self.__class__.__name__
|
|
45
|
+
))
|
|
46
|
+
|
|
47
|
+
return self.name < other.name
|
|
48
|
+
|
|
49
|
+
def resolve(self):
|
|
50
|
+
"""Retrieve and return correct User or Group instance from UserGroup
|
|
51
|
+
|
|
52
|
+
.. versionadded:: 2.16.1
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
User | Group: Resolved User or Group instance
|
|
56
|
+
"""
|
|
57
|
+
# Skip resolving if not a generic instance
|
|
58
|
+
if self.__class__ is not UserGroup:
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
else:
|
|
62
|
+
try:
|
|
63
|
+
return self._swimlane.users.get(id=self.id)
|
|
64
|
+
except ValueError:
|
|
65
|
+
return self._swimlane.groups.get(id=self.id)
|
|
66
|
+
|
|
67
|
+
def as_usergroup_selection(self):
|
|
68
|
+
"""Converts UserGroup to raw UserGroupSelection for populating record
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
dict: Formatted UserGroup data as used by selection fields
|
|
72
|
+
"""
|
|
73
|
+
return {
|
|
74
|
+
'$type': 'Core.Models.Utilities.UserGroupSelection, Core',
|
|
75
|
+
'id': self.id,
|
|
76
|
+
'name': self.name
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def for_json(self):
|
|
80
|
+
"""Get JSON-compatible representation"""
|
|
81
|
+
return {
|
|
82
|
+
'id': self.id,
|
|
83
|
+
'name': self.name
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Group(UserGroup):
|
|
88
|
+
"""Swimlane group record
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
description (str): Group description
|
|
92
|
+
users (GroupUsersCursor): List of users belonging to group.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
_type = 'Core.Models.Groups.Group, Core'
|
|
96
|
+
|
|
97
|
+
def __init__(self, swimlane, raw):
|
|
98
|
+
super(Group, self).__init__(swimlane, raw)
|
|
99
|
+
self.__user_ids = [item['id'] for item in self._raw.get('users')]
|
|
100
|
+
self.description = self._raw.get('description')
|
|
101
|
+
self.__users = None
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def users(self):
|
|
105
|
+
"""Returns a GroupUsersCursor with list of User instances for this Group
|
|
106
|
+
|
|
107
|
+
.. versionadded:: 2.16.2
|
|
108
|
+
"""
|
|
109
|
+
if self.__users is None:
|
|
110
|
+
self.__users = GroupUsersCursor(swimlane=self._swimlane, user_ids=self.__user_ids)
|
|
111
|
+
return self.__users
|
|
112
|
+
|
|
113
|
+
def get_cache_index_keys(self):
|
|
114
|
+
return {
|
|
115
|
+
'id': self.id,
|
|
116
|
+
'name': self.name
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class User(UserGroup):
|
|
121
|
+
"""Swimlane user record
|
|
122
|
+
|
|
123
|
+
Attributes:
|
|
124
|
+
username (str): Unique username
|
|
125
|
+
display_name (str): User display name
|
|
126
|
+
email (str): User email
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
_type = 'Core.Models.Identity.ApplicationUser, Core'
|
|
130
|
+
|
|
131
|
+
def __init__(self, swimlane, raw):
|
|
132
|
+
super(User, self).__init__(swimlane, raw)
|
|
133
|
+
|
|
134
|
+
self.username = self._raw.get('userName')
|
|
135
|
+
self.display_name = self._raw.get('displayName')
|
|
136
|
+
self.email = self._raw.get('email')
|
|
137
|
+
|
|
138
|
+
def get_cache_index_keys(self):
|
|
139
|
+
return {
|
|
140
|
+
'id': self.id,
|
|
141
|
+
'username': self.username,
|
|
142
|
+
'display_name': self.display_name
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class GroupUsersCursor(SwimlaneResolver, Cursor):
|
|
147
|
+
"""Handles retrieval for user endpoint"""
|
|
148
|
+
|
|
149
|
+
def __init__(self, swimlane, user_ids):
|
|
150
|
+
SwimlaneResolver.__init__(self, swimlane)
|
|
151
|
+
Cursor.__init__(self)
|
|
152
|
+
self.__user_ids = user_ids
|
|
153
|
+
|
|
154
|
+
def _evaluate(self):
|
|
155
|
+
"""Lazily retrieve and build User instances from returned data"""
|
|
156
|
+
if self._elements:
|
|
157
|
+
for element in self._elements:
|
|
158
|
+
yield element
|
|
159
|
+
else:
|
|
160
|
+
for user_id in self.__user_ids:
|
|
161
|
+
try:
|
|
162
|
+
element = self._swimlane.users.get(id=user_id)
|
|
163
|
+
self._elements.append(element)
|
|
164
|
+
yield element
|
|
165
|
+
except StopIteration:
|
|
166
|
+
return
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Provides handful of constants used in record reports/searches"""
|
|
2
|
+
|
|
3
|
+
# Filter
|
|
4
|
+
EQ = "equals"
|
|
5
|
+
NOT_EQ = "doesNotEqual"
|
|
6
|
+
CONTAINS = "contains"
|
|
7
|
+
EXCLUDES = "excludes"
|
|
8
|
+
GT = "greaterThan"
|
|
9
|
+
LT = "lessThan"
|
|
10
|
+
LTE = "lessThanOrEqual"
|
|
11
|
+
GTE = "greaterThanOrEqual"
|
|
12
|
+
|
|
13
|
+
# Aggregate
|
|
14
|
+
AVG = "average"
|
|
15
|
+
COUNT = "count"
|
|
16
|
+
SUM = "sum"
|
|
17
|
+
MIN = "min"
|
|
18
|
+
MAX = "max"
|
|
19
|
+
|
|
20
|
+
# GroupBy
|
|
21
|
+
GB = "groupBy"
|
|
22
|
+
HOUR = "groupByHour"
|
|
23
|
+
DAY = "groupByDay"
|
|
24
|
+
WEEK = "groupByWeek"
|
|
25
|
+
MONTH = "groupByMonth"
|
|
26
|
+
QUARTER = "groupByQuarter"
|
|
27
|
+
YEAR = "groupByYear"
|
|
28
|
+
|
|
29
|
+
# Sorts
|
|
30
|
+
ASC = "ascending"
|
|
31
|
+
DESC = "descending"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
class WrappedSession(requests.Session):
|
|
4
|
+
"""A wrapper for requests.Session to override 'verify' property, ignoring REQUESTS_CA_BUNDLE environment variable.
|
|
5
|
+
|
|
6
|
+
This is a workaround for https://github.com/kennethreitz/requests/issues/3829 (will be fixed in requests 3.0.0)
|
|
7
|
+
"""
|
|
8
|
+
def merge_environment_settings(self, url, proxies, stream, verify, *args, **kwargs):
|
|
9
|
+
if self.verify is False:
|
|
10
|
+
verify = False
|
|
11
|
+
|
|
12
|
+
return super(WrappedSession, self).merge_environment_settings(url, proxies, stream, verify, *args, **kwargs)
|