@outliant/sunrise-utils 1.1.7 → 1.1.9

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/CHANGELOG.md CHANGED
@@ -4,9 +4,29 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### [1.1.9](https://github.com/outliant/sunrise-utils/compare/1.1.8...1.1.9)
8
+
9
+ - Task/task new columns #85ztbmnkf [`#14`](https://github.com/outliant/sunrise-utils/pull/14)
10
+ - chore: add filter for days to complete [`f2fdf97`](https://github.com/outliant/sunrise-utils/commit/f2fdf977c6b5c5f22c17b3fffb7a6a2024a3c38c)
11
+ - chore: add test [`847deb3`](https://github.com/outliant/sunrise-utils/commit/847deb3d2eebba343b865bb69cb6dc642096160d)
12
+ - chore: fix test [`ad49d34`](https://github.com/outliant/sunrise-utils/commit/ad49d3451ff523426672528f42f65f77f2d05c5f)
13
+
14
+ #### [1.1.8](https://github.com/outliant/sunrise-utils/compare/1.1.7...1.1.8)
15
+
16
+ > 29 September 2023
17
+
18
+ - chore: use split string helper [`#13`](https://github.com/outliant/sunrise-utils/pull/13)
19
+ - chore: update logic for user pipeline [`#12`](https://github.com/outliant/sunrise-utils/pull/12)
20
+ - chore(release): 1.1.8 [`aa38c1a`](https://github.com/outliant/sunrise-utils/commit/aa38c1abac2d22fddb329fc18e071881b15837b3)
21
+ - chore: update users pipeline [`c0de2e6`](https://github.com/outliant/sunrise-utils/commit/c0de2e6bbacbeb1b8b96597473679f7f2424880e)
22
+ - chore: fix unit test [`84b880d`](https://github.com/outliant/sunrise-utils/commit/84b880d39497aa03257869702ecd0aa9f05b1d77)
23
+
7
24
  #### [1.1.7](https://github.com/outliant/sunrise-utils/compare/1.1.6...1.1.7)
8
25
 
26
+ > 26 September 2023
27
+
9
28
  - chore: update users pipeline [`#11`](https://github.com/outliant/sunrise-utils/pull/11)
29
+ - chore(release): 1.1.7 [`056a962`](https://github.com/outliant/sunrise-utils/commit/056a962aedcd0f9ee8e71cca2d1003db01a69057)
10
30
  - chore: remove map [`6c6972b`](https://github.com/outliant/sunrise-utils/commit/6c6972b89bd60cb44bb48613ad206b785e1f1771)
11
31
  - chore: use buildProjectFilter [`0cae6ce`](https://github.com/outliant/sunrise-utils/commit/0cae6ce4ca13dc56b98e9b98a6c6cff9881824c2)
12
32
 
@@ -0,0 +1,9 @@
1
+ const isEmailValid = (email) => {
2
+ // The regular expression to match a valid email address.
3
+ const regex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z]+$/;
4
+
5
+ // Return true if the string matches the regular expression, false otherwise.
6
+ return regex.test(email);
7
+ };
8
+
9
+ module.exports = isEmailValid;
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'is_equal':
@@ -21,7 +23,7 @@ module.exports.isEqual = (filter) => {
21
23
  let value = filter.value;
22
24
 
23
25
  if (typeof value === 'string') {
24
- value = value.split(',');
26
+ value = splitString(value);
25
27
  }
26
28
 
27
29
  return {
@@ -43,7 +45,7 @@ module.exports.containsAny = (filter) => {
43
45
  let value = filter.value;
44
46
 
45
47
  if (typeof value === 'string') {
46
- value = value.split(',');
48
+ value = splitString(value);
47
49
  }
48
50
 
49
51
  return {
@@ -65,7 +67,7 @@ module.exports.containsNone = (filter) => {
65
67
  let value = filter.value;
66
68
 
67
69
  if (typeof value === 'string') {
68
- value = value.split(',');
70
+ value = splitString(value);
69
71
  }
70
72
 
71
73
  return {
@@ -1,5 +1,6 @@
1
1
  const moment = require('moment');
2
2
  const { escapeElasticQuery } = require('../es');
3
+ const splitString = require('../splitString');
3
4
 
4
5
  function getContainsMappingValue(filter) {
5
6
  if (['text', 'textarea'].includes(filter.field_type)) {
@@ -162,7 +163,7 @@ module.exports.isAnyOf = (filter) => {
162
163
  let value = filter.value;
163
164
 
164
165
  if (typeof value === 'string') {
165
- value = value.split(';');
166
+ value = splitString(value);
166
167
  }
167
168
 
168
169
  if (filter.field_id) {
@@ -356,7 +357,7 @@ module.exports.isEqual = (filter) => {
356
357
  let value = filter.value;
357
358
 
358
359
  if (typeof value === 'string') {
359
- value = value.split(';');
360
+ value = splitString(value);
360
361
  }
361
362
 
362
363
  mappingValue = {
@@ -1,5 +1,6 @@
1
1
  const moment = require('moment');
2
2
  const { escapeElasticQuery } = require('../es');
3
+ const splitString = require('../splitString');
3
4
 
4
5
  module.exports = (filter) => {
5
6
  switch (filter.condition) {
@@ -127,7 +128,7 @@ module.exports.isEqual = (filter) => {
127
128
 
128
129
  if (filter.field_type === 'checkbox') {
129
130
  const value = typeof filter.value === 'string'
130
- ? filter.value.split(';')
131
+ ? splitString(filter.value)
131
132
  : filter.value;
132
133
 
133
134
  const should = [];
@@ -160,7 +161,7 @@ module.exports.isEqual = (filter) => {
160
161
 
161
162
  return {
162
163
  term: {
163
- 'fields.text': {
164
+ [filter.type]: {
164
165
  value: filter.value
165
166
  }
166
167
  }
@@ -383,7 +384,7 @@ module.exports.isNotEqual = (filter) => {
383
384
 
384
385
  if (filter.field_type === 'checkbox') {
385
386
  const value = typeof filter.value === 'string'
386
- ? filter.value.split(';')
387
+ ? splitString(filter.value)
387
388
  : filter.value;
388
389
 
389
390
  const should = [];
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'contains_any':
@@ -15,7 +17,7 @@ module.exports.containsAny = (filter) => {
15
17
  let value = filter.value;
16
18
 
17
19
  if (typeof value === 'string') {
18
- value = value.split(',');
20
+ value = splitString(value);
19
21
  }
20
22
 
21
23
  return {
@@ -37,7 +39,7 @@ module.exports.containsNone = (filter) => {
37
39
  let value = filter.value;
38
40
 
39
41
  if (typeof value === 'string') {
40
- value = value.split(',');
42
+ value = splitString(value);
41
43
  }
42
44
 
43
45
  return {
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'contains_any':
@@ -18,7 +20,7 @@ module.exports.containsAny = (filter) => {
18
20
  let value = filter.value;
19
21
 
20
22
  if (typeof value === 'string') {
21
- value = value.split(',');
23
+ value = splitString(value);
22
24
  }
23
25
 
24
26
  return {
@@ -40,7 +42,7 @@ module.exports.containsNone = (filter) => {
40
42
  let value = filter.value;
41
43
 
42
44
  if (typeof value === 'string') {
43
- value = value.split(',');
45
+ value = splitString(value);
44
46
  }
45
47
 
46
48
  return {
@@ -62,7 +64,7 @@ module.exports.containsAll = (filter) => {
62
64
  let value = filter.value;
63
65
 
64
66
  if (typeof value === 'string') {
65
- value = value.split(',');
67
+ value = splitString(value);
66
68
  }
67
69
 
68
70
  return {
@@ -210,7 +210,7 @@ module.exports.criticalPathStage = (order) => {
210
210
  if (stageIndex != null) {
211
211
  return stageIndex;
212
212
  }
213
-
213
+
214
214
  return 9999999;
215
215
  } catch (Exception err) {
216
216
  return 9999999;
@@ -318,6 +318,39 @@ module.exports.projectFieldNumeric = (order, value) => {
318
318
  };
319
319
  };
320
320
 
321
+ module.exports.daysToComplete = (order) => {
322
+ return {
323
+ _script: {
324
+ type: 'number',
325
+ script: {
326
+ lang: 'painless',
327
+ source: `
328
+ try {
329
+ def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;
330
+ def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;
331
+
332
+ if (isEmptyReadyAt || isEmptyCompletedAt) {
333
+ return -999999;
334
+ }
335
+
336
+ def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();
337
+ def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();
338
+
339
+ if (readyAt == null || completedAt == null) {
340
+ return -999999;
341
+ }
342
+
343
+ return completedAt - readyAt;
344
+ } catch (Exception err) {
345
+ return -999999;
346
+ }
347
+ `
348
+ },
349
+ order
350
+ }
351
+ };
352
+ };
353
+
321
354
  module.exports.projectFieldText = (order, value) => {
322
355
  return {
323
356
  _script: {
@@ -0,0 +1,6 @@
1
+ function splitString(str, pattern = /[,;]/) {
2
+ const delimiterPattern = pattern; // Match either ',' or ';'
3
+ return str.split(delimiterPattern);
4
+ }
5
+
6
+ module.exports = splitString;
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'is_empty':
@@ -106,7 +108,7 @@ module.exports.isAnyOf = (filter) => {
106
108
  let value = filter.value;
107
109
 
108
110
  if (typeof value === 'string') {
109
- value = value.split(';');
111
+ value = splitString(value);
110
112
  }
111
113
 
112
114
  return {
@@ -0,0 +1,220 @@
1
+ const _ = require('lodash');
2
+
3
+ const commonOperatorTemplate = (conditionCallback) => {
4
+ const condition = conditionCallback({
5
+ daysToComplete: 'daysToComplete',
6
+ value: 'params.value',
7
+ secondValue: 'params.second_value'
8
+ });
9
+
10
+ return `
11
+ try {
12
+ def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;
13
+ def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;
14
+
15
+ if (isEmptyReadyAt || isEmptyCompletedAt) {
16
+ return false;
17
+ }
18
+
19
+ def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();
20
+ def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();
21
+
22
+ if (readyAt == null || completedAt == null) {
23
+ return false;
24
+ }
25
+
26
+ def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);
27
+
28
+ return ${condition}
29
+ } catch (Exception err) {
30
+ return false;
31
+ }
32
+ `;
33
+ };
34
+
35
+ module.exports = (filter) => {
36
+ filter.value = _.parseInt(filter.value);
37
+ filter.second_value = _.parseInt(filter.second_value);
38
+
39
+ switch (filter.condition) {
40
+ case 'is_equal':
41
+ return module.exports.isEqual(filter);
42
+ case 'not_equal':
43
+ return module.exports.isNotEqual(filter);
44
+ case 'less_than':
45
+ return module.exports.lessThan(filter);
46
+ case 'less_than_or_equal':
47
+ return module.exports.lessThanOrEqual(filter);
48
+ case 'greater_than':
49
+ return module.exports.greaterThan(filter);
50
+ case 'greater_than_or_equal':
51
+ return module.exports.greaterThanOrEqual(filter);
52
+ case 'between':
53
+ return module.exports.between(filter);
54
+ case 'is_empty':
55
+ return module.exports.isEmpty(filter);
56
+ case 'is_not_empty':
57
+ return module.exports.isNotEmpty(filter);
58
+ default:
59
+ return null;
60
+ }
61
+ };
62
+
63
+ module.exports.isEqual = (filter) => {
64
+ return {
65
+ script: {
66
+ script: {
67
+ source: commonOperatorTemplate(({ daysToComplete, value }) => {
68
+ return `${daysToComplete} == ${value}`;
69
+ }),
70
+ lang: "painless",
71
+ params: {
72
+ value: filter.value,
73
+ }
74
+ }
75
+ }
76
+ };
77
+
78
+ };
79
+
80
+ module.exports.isNotEqual = (filter) => {
81
+ return {
82
+ script: {
83
+ script: {
84
+ source: commonOperatorTemplate(({ daysToComplete, value }) => {
85
+ return `${daysToComplete} != ${value}`;
86
+ }),
87
+ lang: "painless",
88
+ params: {
89
+ value: filter.value,
90
+ }
91
+ }
92
+ }
93
+ };
94
+
95
+ };
96
+
97
+ module.exports.greaterThan = (filter) => {
98
+ return {
99
+ script: {
100
+ script: {
101
+ source: commonOperatorTemplate(({ daysToComplete, value }) => {
102
+ return `${daysToComplete} > ${value}`;
103
+ }),
104
+ lang: "painless",
105
+ params: {
106
+ value: filter.value,
107
+ }
108
+ }
109
+ }
110
+ };
111
+ };
112
+
113
+ module.exports.greaterThanOrEqual = (filter) => {
114
+ return {
115
+ script: {
116
+ script: {
117
+ source: commonOperatorTemplate(({ daysToComplete, value }) => {
118
+ return `${daysToComplete} >= ${value}`;
119
+ }),
120
+ lang: "painless",
121
+ params: {
122
+ value: filter.value,
123
+ }
124
+ }
125
+ }
126
+ };
127
+ };
128
+
129
+ module.exports.lessThan = (filter) => {
130
+ return {
131
+ script: {
132
+ script: {
133
+ source: commonOperatorTemplate(({ daysToComplete, value }) => {
134
+ return `${daysToComplete} < ${value}`;
135
+ }),
136
+ lang: "painless",
137
+ params: {
138
+ value: filter.value,
139
+ }
140
+ }
141
+ }
142
+ };
143
+ };
144
+
145
+ module.exports.lessThanOrEqual = (filter) => {
146
+ return {
147
+ script: {
148
+ script: {
149
+ source: commonOperatorTemplate(({ daysToComplete, value }) => {
150
+ return `${daysToComplete} <= ${value}`;
151
+ }),
152
+ lang: "painless",
153
+ params: {
154
+ value: filter.value,
155
+ second_value: filter.second_value
156
+ }
157
+ }
158
+ }
159
+ };
160
+ };
161
+
162
+ module.exports.between = (filter) => {
163
+ return {
164
+ script: {
165
+ script: {
166
+ source: commonOperatorTemplate(({ daysToComplete, value, secondValue }) => {
167
+ return `
168
+ ${daysToComplete} >= ${value} &&
169
+ ${daysToComplete} <= ${secondValue}
170
+ `;
171
+ }),
172
+ lang: "painless",
173
+ params: {
174
+ value: filter.value,
175
+ second_value: filter.second_value
176
+ }
177
+ }
178
+ }
179
+ };
180
+ };
181
+
182
+ module.exports.isEmpty = () => {
183
+ return {
184
+ script: {
185
+ script: {
186
+ source: `
187
+ try {
188
+ def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;
189
+ def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;
190
+
191
+ return isEmptyReadyAt || isEmptyCompletedAt;
192
+ } catch (Exception err) {
193
+ return false;
194
+ }
195
+ `,
196
+ lang: "painless"
197
+ }
198
+ }
199
+ };
200
+ };
201
+
202
+ module.exports.isNotEmpty = () => {
203
+ return {
204
+ script: {
205
+ script: {
206
+ source: `
207
+ try {
208
+ def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;
209
+ def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;
210
+
211
+ return !isEmptyReadyAt && !isEmptyCompletedAt;
212
+ } catch (Exception err) {
213
+ return false;
214
+ }
215
+ `,
216
+ lang: "painless"
217
+ }
218
+ }
219
+ };
220
+ };
@@ -5,9 +5,11 @@ const buildRegionFilter = require('./regionFilter');
5
5
  const buildTaskFieldFilter = require('./taskFieldFilter');
6
6
  const buildCriticalPathFilter = require('./criticalPathFilter');
7
7
  const buildOnHoldFilter = require('./onHoldFilter');
8
+ const buildDaysToCompleteFilter = require('./daysToComplete');
8
9
  const buildAgeColorFilter = require('./buildAgeColorFilter');
9
10
  const buildProjectFieldFilter = require('../projectFilter/projectFieldFilter');
10
11
  const { PROJECT_BANNERS } = require('../../constants');
12
+ const splitString = require('../splitString');
11
13
 
12
14
  module.exports.taskStatusOptions = [
13
15
  {
@@ -65,6 +67,18 @@ module.exports.defaultCustomColumns = [
65
67
  type: 'ready_at',
66
68
  field_type: 'date'
67
69
  },
70
+ {
71
+ name: 'Days to Complete',
72
+ value: 'days_to_complete',
73
+ type: 'days_to_complete',
74
+ field_type: 'number'
75
+ },
76
+ {
77
+ name: 'Pending Date',
78
+ value: 'pending_date',
79
+ type: 'pending_date',
80
+ field_type: 'date'
81
+ },
68
82
  {
69
83
  name: 'Project ID',
70
84
  value: 'project_id',
@@ -221,17 +235,24 @@ module.exports.buildTaskPipelineFilter = (filter) => {
221
235
  return buildCriticalPathFilter(filter);
222
236
  case 'on_hold':
223
237
  return buildOnHoldFilter(filter);
238
+ case 'days_to_complete':
239
+ return buildDaysToCompleteFilter(filter);
224
240
  // Always expected as is_equal condition
225
241
  case 'path_age':
226
242
  return buildPathAgeFilter(filter);
227
243
  case 'task_age':
228
244
  return buildAgeColorFilter(filter);
245
+ case 'pending_date':
246
+ return buildTaskFieldFilter({
247
+ ...filter,
248
+ type: 'ready_at'
249
+ });
229
250
  // Always expected as is_equal condition with array of task template ids
230
251
  case 'task_templates':
231
252
  let value = filter.value;
232
253
 
233
254
  if (typeof value === 'string') {
234
- value = value.split(';');
255
+ value = splitString(value);
235
256
  }
236
257
 
237
258
  return {
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'is_empty':
@@ -79,7 +81,7 @@ module.exports.isAnyOf = (filter) => {
79
81
  let value = filter.value;
80
82
 
81
83
  if (typeof value === 'string') {
82
- value = value.split(';');
84
+ value = splitString(value);
83
85
  }
84
86
 
85
87
  const should = [];
@@ -1,5 +1,7 @@
1
1
  const { validate: isUuid } = require('uuid');
2
2
 
3
+ const splitString = require('../splitString');
4
+
3
5
  module.exports = (filter) => {
4
6
  switch (filter.condition) {
5
7
  case 'is_empty':
@@ -101,7 +103,7 @@ module.exports.isAnyOf = (filter) => {
101
103
  let value = filter.value;
102
104
 
103
105
  if (typeof value === 'string') {
104
- value = value.split(';');
106
+ value = splitString(value);
105
107
  }
106
108
 
107
109
  const should = [];
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'is_empty':
@@ -45,7 +47,7 @@ module.exports.isEqual = (filter) => {
45
47
  let value = filter.value;
46
48
 
47
49
  if (typeof value === 'string') {
48
- value = value.split(';');
50
+ value = splitString(value);
49
51
  }
50
52
 
51
53
  return {
@@ -1,3 +1,5 @@
1
+ const splitString = require('../splitString');
2
+
1
3
  module.exports = (filter) => {
2
4
  switch (filter.condition) {
3
5
  case 'is_empty':
@@ -142,7 +144,7 @@ module.exports.isAnyOf = (filter) => {
142
144
  let value = filter.value;
143
145
 
144
146
  if (typeof value === 'string') {
145
- value = value.split(';');
147
+ value = splitString(value);
146
148
  }
147
149
 
148
150
  const should = [];
@@ -1,5 +1,7 @@
1
1
  const moment = require('moment');
2
2
 
3
+ const splitString = require('../splitString');
4
+
3
5
  module.exports = (filter) => {
4
6
  switch (filter.condition) {
5
7
  case 'contains_any':
@@ -97,7 +99,7 @@ module.exports.isAnyOf = (filter) => {
97
99
  let value = filter.value;
98
100
 
99
101
  if (typeof value === 'string') {
100
- value = value.split(';');
102
+ value = splitString(value);
101
103
  }
102
104
 
103
105
  return {
@@ -98,30 +98,61 @@ class TaskPipeline {
98
98
  } else {
99
99
  sortByMultiple.forEach((s, i) => {
100
100
  const sort = sortMultiple[i] || 'desc';
101
- if (s === 'region') {
102
- taskPipelinesSort.push(sortScript.region(sort));
103
- } else if (s === 'owner' || s === 'completed_by') {
104
- taskPipelinesSort.push(sortScript.owner(sort));
105
- } else if (s === 'status') {
106
- taskPipelinesSort.push(sortScript.status(sort));
107
- } else if (s === 'critical_path_stage') {
108
- taskPipelinesSort.push(sortScript.criticalPathStage(sort));
109
- } else if (s === 'project_id') {
110
- // Sort numeric part of the Project ID
111
- taskPipelinesSort.push(sortScript.projectId(sort));
112
- } else if (s === 'ready_at') {
113
- taskPipelinesSort.push(sortScript.readyAt(sort));
114
- } else if (s === 'was_marked_incomplete') {
115
- taskPipelinesSort.push(sortScript.wasMarkedIncomplete(sort));
116
- } else if (s === 'last_comment_date') {
117
- taskPipelinesSort.push(sortScript.lastCommentDate(sort));
118
- } else if (isUuid(s)) {
119
- taskPipelinesSort.push(sortScript.projectFieldNumeric(sort, s));
120
- taskPipelinesSort.push(sortScript.projectFieldText(sort, s));
121
- } else {
122
- taskPipelinesSort.push({
123
- [s]: { order: sort, missing: '_last' }
124
- });
101
+
102
+ switch (true) {
103
+ case s === 'region':
104
+ taskPipelinesSort.push(sortScript.region(sort));
105
+ break;
106
+
107
+ case s === 'owner' :
108
+ case s === 'completed_by' :
109
+ taskPipelinesSort.push(sortScript.owner(sort));
110
+ break;
111
+
112
+ case s === 'status':
113
+ taskPipelinesSort.push(sortScript.status(sort));
114
+ break;
115
+
116
+ case s === 'critical_path_stage':
117
+ taskPipelinesSort.push(sortScript.criticalPathStage(sort));
118
+ break;
119
+
120
+ case s === 'project_id':
121
+ taskPipelinesSort.push(sortScript.projectId(sort));
122
+ break;
123
+
124
+ case s === 'ready_at':
125
+ taskPipelinesSort.push(sortScript.readyAt(sort));
126
+ break;
127
+
128
+ case s === 'pending_date':
129
+ taskPipelinesSort.push({
130
+ ready_at: { order: sort, missing: '_last' }
131
+ });
132
+ break;
133
+
134
+ case s === 'days_to_complete':
135
+ taskPipelinesSort.push(sortScript.daysToComplete(sort));
136
+ break;
137
+
138
+ case s === 'was_marked_incomplete':
139
+ taskPipelinesSort.push(sortScript.wasMarkedIncomplete(sort));
140
+ break;
141
+
142
+ case s === 'last_comment_date':
143
+ taskPipelinesSort.push(sortScript.lastCommentDate(sort));
144
+ break;
145
+
146
+ case isUuid(s):
147
+ taskPipelinesSort.push(sortScript.projectFieldNumeric(sort, s));
148
+ taskPipelinesSort.push(sortScript.projectFieldText(sort, s));
149
+ break;
150
+
151
+ default:
152
+ taskPipelinesSort.push({
153
+ [s]: { order: sort, missing: '_last' }
154
+ });
155
+ break;
125
156
  }
126
157
  });
127
158
  }
@@ -1,12 +1,44 @@
1
1
  const sortScript = require('../helpers/users/sortScript');
2
2
  const buildProjectFilter = require('../helpers/projectFilter/projectFilter');
3
+ const isEmailValid = require('../helpers/isEmailValid');
3
4
 
4
5
  // customer filters
5
6
  const buildStatusFilter = require('../helpers/users/filters/status');
6
7
 
7
8
  class UsersPipeline {
8
- buildFiltersQuery({ filters }) {
9
- const mustQuery = [];
9
+ buildFiltersQuery (
10
+ organizationId,
11
+ filters,
12
+ search
13
+ ) {
14
+ const mustQuery = [
15
+ {
16
+ term: {
17
+ organizationId: {
18
+ value: organizationId
19
+ }
20
+ }
21
+ }
22
+ ];
23
+
24
+ if (search) {
25
+ if (isEmailValid(search)) {
26
+ mustQuery.push({
27
+ match: {
28
+ email: search
29
+ }
30
+ });
31
+ } else {
32
+ mustQuery.push(
33
+ {
34
+ query_string: {
35
+ fields: ['name.raw', 'email.raw'],
36
+ query: `*${search}*`,
37
+ default_operator: 'AND'
38
+ }
39
+ });
40
+ }
41
+ }
10
42
 
11
43
  for (const filter of filters) {
12
44
  switch (filter.type) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@outliant/sunrise-utils",
3
3
  "description": "Helper functions for project Sunrise",
4
- "version": "1.1.7",
4
+ "version": "1.1.9",
5
5
  "license": "ISC",
6
6
  "author": "Outliant",
7
7
  "main": "index.js",
@@ -0,0 +1,160 @@
1
+ 'use strict';
2
+
3
+ /* eslint-env node, mocha */
4
+ const { expect } = require('chai');
5
+
6
+ // To test
7
+ const daysToComplete = require('../../../helpers/taskFilter/daysToComplete');
8
+
9
+ describe('taskFilter/daysToComplete', function () {
10
+ const commonInput = {
11
+ field_id: '',
12
+ type: 'days_to_complete',
13
+ field_type: 'number',
14
+ value: 1,
15
+ second_value: 5
16
+ };
17
+
18
+ const filters = [
19
+ {
20
+ input: {
21
+ ...commonInput,
22
+ condition: 'not_equal'
23
+ },
24
+ expected: {
25
+ script: {
26
+ script: {
27
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n if (isEmptyReadyAt || isEmptyCompletedAt) {\n return false;\n }\n\n def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();\n def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();\n\n if (readyAt == null || completedAt == null) {\n return false;\n }\n\n def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);\n\n return daysToComplete != params.value\n } catch (Exception err) {\n return false;\n }\n ",
28
+ lang: "painless",
29
+ params: {
30
+ value: 1
31
+ }
32
+ }
33
+ }
34
+ }
35
+ },
36
+ {
37
+ input: {
38
+ ...commonInput,
39
+ condition: 'less_than'
40
+ },
41
+ expected: {
42
+ script: {
43
+ script: {
44
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n if (isEmptyReadyAt || isEmptyCompletedAt) {\n return false;\n }\n\n def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();\n def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();\n\n if (readyAt == null || completedAt == null) {\n return false;\n }\n\n def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);\n\n return daysToComplete < params.value\n } catch (Exception err) {\n return false;\n }\n ",
45
+ lang: "painless",
46
+ params: {
47
+ value: 1
48
+ }
49
+ }
50
+ }
51
+ }
52
+ },
53
+ {
54
+ input: {
55
+ ...commonInput,
56
+ condition: 'less_than_or_equal'
57
+ },
58
+ expected: {
59
+ script: {
60
+ script: {
61
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n if (isEmptyReadyAt || isEmptyCompletedAt) {\n return false;\n }\n\n def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();\n def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();\n\n if (readyAt == null || completedAt == null) {\n return false;\n }\n\n def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);\n\n return daysToComplete <= params.value\n } catch (Exception err) {\n return false;\n }\n ",
62
+ lang: "painless",
63
+ params: {
64
+ value: 1,
65
+ second_value: 5
66
+ }
67
+ }
68
+ }
69
+ }
70
+ },
71
+ {
72
+ input: {
73
+ ...commonInput,
74
+ condition: 'greater_than'
75
+ },
76
+ expected: {
77
+ script: {
78
+ script: {
79
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n if (isEmptyReadyAt || isEmptyCompletedAt) {\n return false;\n }\n\n def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();\n def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();\n\n if (readyAt == null || completedAt == null) {\n return false;\n }\n\n def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);\n\n return daysToComplete > params.value\n } catch (Exception err) {\n return false;\n }\n ",
80
+ lang: "painless",
81
+ params: {
82
+ value: 1
83
+ }
84
+ }
85
+ }
86
+ }
87
+ },
88
+ {
89
+ input: {
90
+ ...commonInput,
91
+ condition: 'greater_than_or_equal'
92
+ },
93
+ expected: {
94
+ script: {
95
+ script: {
96
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n if (isEmptyReadyAt || isEmptyCompletedAt) {\n return false;\n }\n\n def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();\n def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();\n\n if (readyAt == null || completedAt == null) {\n return false;\n }\n\n def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);\n\n return daysToComplete >= params.value\n } catch (Exception err) {\n return false;\n }\n ",
97
+ lang: "painless",
98
+ params: {
99
+ value: 1
100
+ }
101
+ }
102
+ }
103
+ }
104
+ },
105
+ {
106
+ input: {
107
+ ...commonInput,
108
+ condition: 'between'
109
+ },
110
+ expected: {
111
+ script: {
112
+ script: {
113
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n if (isEmptyReadyAt || isEmptyCompletedAt) {\n return false;\n }\n\n def readyAt = doc['ready_at'].value.toInstant().toEpochMilli();\n def completedAt = doc['completed_at'].value.toInstant().toEpochMilli();\n\n if (readyAt == null || completedAt == null) {\n return false;\n }\n\n def daysToComplete = (completedAt - readyAt) / (1000 * 3600 * 24);\n\n return \n daysToComplete >= params.value &&\n daysToComplete <= params.second_value\n \n } catch (Exception err) {\n return false;\n }\n ",
114
+ lang: "painless",
115
+ params: {
116
+ value: 1,
117
+ second_value: 5
118
+ }
119
+ }
120
+ }
121
+ }
122
+ },
123
+ {
124
+ input: {
125
+ ...commonInput,
126
+ condition: 'is_empty'
127
+ },
128
+ expected: {
129
+ script: {
130
+ script: {
131
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n return isEmptyReadyAt || isEmptyCompletedAt;\n } catch (Exception err) {\n return false;\n }\n ",
132
+ lang: "painless"
133
+ }
134
+ }
135
+ }
136
+ },
137
+ {
138
+ input: {
139
+ ...commonInput,
140
+ condition: 'is_not_empty'
141
+ },
142
+ expected: {
143
+ script: {
144
+ script: {
145
+ source: "\n try {\n def isEmptyReadyAt = doc.containsKey('ready_at') && doc['ready_at'].empty;\n def isEmptyCompletedAt = doc.containsKey('completed_at') && doc['completed_at'].empty;\n\n return !isEmptyReadyAt && !isEmptyCompletedAt;\n } catch (Exception err) {\n return false;\n }\n ",
146
+ lang: "painless"
147
+ }
148
+ }
149
+ }
150
+ }
151
+ ];
152
+
153
+ filters.forEach((filter) => {
154
+ it(filter.input.condition, (done) => {
155
+ expect(daysToComplete(filter.input)).to.deep.equal(filter.expected);
156
+
157
+ done();
158
+ });
159
+ });
160
+ });
@@ -257,123 +257,136 @@ describe('taskPipeline', function () {
257
257
  }
258
258
  ]
259
259
  );
260
+
260
261
  expect(sort).to.deep.equal([
261
262
  {
262
263
  _script: {
263
- type: 'number',
264
+ type: "number",
264
265
  script: {
265
- lang: 'painless',
266
- source:
267
- '\n if (params.starredTaskIds.contains(doc._id.value)) {\n return 1;\n } else {\n return 0;\n }\n ',
268
- params: { starredTaskIds: ['task-1', 'task-2'] }
266
+ lang: "painless",
267
+ source: "\n if (params.starredTaskIds.contains(doc._id.value)) {\n return 1;\n } else {\n return 0;\n }\n ",
268
+ params: {
269
+ starredTaskIds: [
270
+ "task-1",
271
+ "task-2"
272
+ ]
273
+ }
269
274
  },
270
- order: 'desc'
275
+ order: "desc"
276
+ }
277
+ },
278
+ {
279
+ region_name: {
280
+ order: "asc"
271
281
  }
272
282
  },
273
- { region_name: { order: 'asc' } },
274
283
  {
275
284
  _script: {
276
- type: 'string',
285
+ type: "string",
277
286
  script: {
278
- lang: 'painless',
279
- source:
280
- "\n try {\n String owner = doc['owner'].value;\n\n if (owner != '' && owner != null) {\n return owner.toLowerCase();\n }\n } catch (Exception err) {}\n\n return '';\n "
287
+ lang: "painless",
288
+ source: "\n try {\n String owner = doc['owner'].value;\n\n if (owner != '' && owner != null) {\n return owner.toLowerCase();\n }\n } catch (Exception err) {}\n\n return '';\n "
281
289
  },
282
- order: 'asc'
290
+ order: "asc"
283
291
  }
284
292
  },
285
293
  {
286
294
  _script: {
287
- type: 'string',
295
+ type: "string",
288
296
  script: {
289
- lang: 'painless',
290
- source:
291
- "\n try {\n String owner = doc['owner'].value;\n\n if (owner != '' && owner != null) {\n return owner.toLowerCase();\n }\n } catch (Exception err) {}\n\n return '';\n "
297
+ lang: "painless",
298
+ source: "\n try {\n String owner = doc['owner'].value;\n\n if (owner != '' && owner != null) {\n return owner.toLowerCase();\n }\n } catch (Exception err) {}\n\n return '';\n "
292
299
  },
293
- order: 'asc'
300
+ order: "asc"
294
301
  }
295
302
  },
296
303
  {
297
304
  _script: {
298
- type: 'string',
305
+ type: "string",
299
306
  script: {
300
- lang: 'painless',
301
- source:
302
- "\n try {\n boolean isReady = doc['is_ready'].value;\n boolean isComplete = doc['is_complete'].value;\n\n if (isReady == true) {\n if (isComplete == true) {\n return 'completed';\n }\n\n return 'pending';\n }\n\n return 'new';\n } catch (Exception err) {\n return '';\n }\n "
307
+ lang: "painless",
308
+ source: "\n try {\n boolean isReady = doc['is_ready'].value;\n boolean isComplete = doc['is_complete'].value;\n\n if (isReady == true) {\n if (isComplete == true) {\n return 'completed';\n }\n\n return 'pending';\n }\n\n return 'new';\n } catch (Exception err) {\n return '';\n }\n "
303
309
  },
304
- order: 'desc'
310
+ order: "desc"
305
311
  }
306
312
  },
307
313
  {
308
314
  _script: {
309
- type: 'number',
315
+ type: "number",
310
316
  script: {
311
- lang: 'painless',
312
- source:
313
- "\n try {\n def stageIndex = doc['critical_path.stage_index'].value;\n if (stageIndex != null) {\n return stageIndex;\n }\n \n return 9999999;\n } catch (Exception err) {\n return 9999999;\n }\n "
317
+ lang: "painless",
318
+ source: "\n try {\n def stageIndex = doc['critical_path.stage_index'].value;\n if (stageIndex != null) {\n return stageIndex;\n }\n\n return 9999999;\n } catch (Exception err) {\n return 9999999;\n }\n "
314
319
  },
315
- order: 'desc'
320
+ order: "desc"
316
321
  }
317
322
  },
318
323
  {
319
324
  _script: {
320
- type: 'number',
325
+ type: "number",
321
326
  script: {
322
- lang: 'painless',
323
- source:
324
- "\n def projectNumber = params._source.project_id;\n projectNumber = /[^0-9]/.matcher(projectNumber).replaceAll('');\n return Integer.parseInt(projectNumber);\n "
327
+ lang: "painless",
328
+ source: "\n def projectNumber = params._source.project_id;\n projectNumber = /[^0-9]/.matcher(projectNumber).replaceAll('');\n return Integer.parseInt(projectNumber);\n "
325
329
  },
326
- order: 'desc'
330
+ order: "desc"
327
331
  }
328
332
  },
329
333
  {
330
334
  _script: {
331
- type: 'number',
335
+ type: "number",
332
336
  script: {
333
- lang: 'painless',
334
- source:
335
- "\n try {\n boolean isReady = doc['is_ready'].value;\n boolean isComplete = doc['is_complete'].value;\n\n if (isReady == true && isComplete == true && doc.containsKey('completed_at') && !doc['completed_at'].empty) {\n return doc['completed_at'].value.millis;\n } else if (isReady == true && doc.containsKey('ready_at') && !doc['ready_at'].empty) {\n return doc['ready_at'].value.millis;\n }\n\n return doc['created_at'].value.millis;\n } catch (Exception err) {\n return 0;\n }\n "
337
+ lang: "painless",
338
+ source: "\n try {\n boolean isReady = doc['is_ready'].value;\n boolean isComplete = doc['is_complete'].value;\n\n if (isReady == true && isComplete == true && doc.containsKey('completed_at') && !doc['completed_at'].empty) {\n return doc['completed_at'].value.millis;\n } else if (isReady == true && doc.containsKey('ready_at') && !doc['ready_at'].empty) {\n return doc['ready_at'].value.millis;\n }\n\n return doc['created_at'].value.millis;\n } catch (Exception err) {\n return 0;\n }\n "
336
339
  },
337
- order: 'desc'
340
+ order: "desc"
338
341
  }
339
342
  },
340
343
  {
341
344
  _script: {
342
- type: 'number',
345
+ type: "number",
343
346
  script: {
344
- lang: 'painless',
345
- source:
346
- "\n return doc['was_marked_incomplete'].empty\n ? 0\n : doc['was_marked_incomplete'].value ? 1 : 0;\n "
347
+ lang: "painless",
348
+ source: "\n return doc['was_marked_incomplete'].empty\n ? 0\n : doc['was_marked_incomplete'].value ? 1 : 0;\n "
347
349
  },
348
- order: 'desc'
350
+ order: "desc"
351
+ }
352
+ },
353
+ {
354
+ "last_comment_data.created_at": {
355
+ order: "desc"
349
356
  }
350
357
  },
351
- { 'last_comment_data.created_at': { order: 'desc' } },
352
358
  {
353
359
  _script: {
354
- type: 'number',
360
+ type: "number",
355
361
  script: {
356
- lang: 'painless',
357
- source:
358
- '\n try {\n def fields = params._source.fields;\n\n for (int i=0; i<fields.size(); i++) {\n if (fields[i].id.equals(params.value)) {\n if (fields[i].number != null) {\n return fields[i].number;\n } else {\n return -999999;\n }\n }\n }\n\n return -999999;\n } catch (Exception err) {\n return -999999;\n }\n ',
359
- params: { value: '38d423bf-fc24-4183-b8b7-15420c89e318' }
362
+ lang: "painless",
363
+ source: "\n try {\n def fields = params._source.fields;\n\n for (int i=0; i<fields.size(); i++) {\n if (fields[i].id.equals(params.value)) {\n if (fields[i].number != null) {\n return fields[i].number;\n } else {\n return -999999;\n }\n }\n }\n\n return -999999;\n } catch (Exception err) {\n return -999999;\n }\n ",
364
+ params: {
365
+ value: "38d423bf-fc24-4183-b8b7-15420c89e318"
366
+ }
360
367
  },
361
- order: 'desc'
368
+ order: "desc"
362
369
  }
363
370
  },
364
371
  {
365
372
  _script: {
366
- type: 'string',
373
+ type: "string",
367
374
  script: {
368
- lang: 'painless',
369
- source:
370
- "\n try {\n String textInfo = '';\n def fields = params._source.fields;\n\n for (int i=0; i<fields.size(); i++) {\n if (fields[i].id.equals(params.value)) {\n\n if (fields[i].number != null) {\n return '';\n }\n\n textInfo = fields[i].text == null ? '' : fields[i].text;\n if (fields[i].containsKey('computedText')) {\n textInfo = fields[i].computedText == null ? textInfo : fields[i].computedText;\n }\n }\n }\n\n return textInfo;\n } catch (Exception err) {\n return '';\n }\n ",
371
- params: { value: '38d423bf-fc24-4183-b8b7-15420c89e318' }
375
+ lang: "painless",
376
+ source: "\n try {\n String textInfo = '';\n def fields = params._source.fields;\n\n for (int i=0; i<fields.size(); i++) {\n if (fields[i].id.equals(params.value)) {\n\n if (fields[i].number != null) {\n return '';\n }\n\n textInfo = fields[i].text == null ? '' : fields[i].text;\n if (fields[i].containsKey('computedText')) {\n textInfo = fields[i].computedText == null ? textInfo : fields[i].computedText;\n }\n }\n }\n\n return textInfo;\n } catch (Exception err) {\n return '';\n }\n ",
377
+ params: {
378
+ value: "38d423bf-fc24-4183-b8b7-15420c89e318"
379
+ }
372
380
  },
373
- order: 'desc'
381
+ order: "desc"
374
382
  }
375
383
  },
376
- { id: { order: 'desc', missing: '_last' } }
384
+ {
385
+ id: {
386
+ order: "desc",
387
+ missing: "_last"
388
+ }
389
+ }
377
390
  ]);
378
391
  done();
379
392
  });