@docbrasil/api-systemmanager 1.2.4 → 1.2.6

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/api/admin/user.js CHANGED
@@ -298,6 +298,90 @@ class AdminUser {
298
298
  }
299
299
  }
300
300
 
301
+ /**
302
+ * @author Myndware <augusto.pissarra@myndware.com>
303
+ * @description Batch-create users from an uploaded Excel (.xlsx) or CSV file.
304
+ *
305
+ * Uploads the file as multipart/form-data. The server parses it, validates
306
+ * headers, de-duplicates emails, admits rows FIFO against the organization's
307
+ * user cap, and delegates the actual creation to the existing registration
308
+ * chain. Response is a per-row result array (created / existing / skipped).
309
+ *
310
+ * Status codes:
311
+ * - 200 when at least one row was created or matched an existing user.
312
+ * - 422 (same JSON body shape) when EVERY row was skipped — callers
313
+ * should promote the 422 response body to a completed result, not an
314
+ * error. Axios throws on 422 by default, so catch and inspect
315
+ * `ex.response.data.results`.
316
+ * - 400 for structural failures (invalid_file, missing_columns, empty_file,
317
+ * too_many_rows) — `response.data.code` carries the machine-readable code.
318
+ * - 403 when the caller does not belong to the target organization or lacks
319
+ * user-admin role (code: 'forbidden').
320
+ * - 413 when the uploaded file exceeds 2 MB.
321
+ *
322
+ * @param {FormData} formData A browser FormData instance with a single field
323
+ * named `file` whose value is the .xlsx or .csv File/Blob. Must be
324
+ * FormData so the browser/axios can set the multipart boundary.
325
+ * @param {string} session JWT session token
326
+ * @return {Promise<object>} Batch result:
327
+ * {
328
+ * total: number,
329
+ * created: number,
330
+ * existing: number,
331
+ * skipped: number,
332
+ * results: Array<{
333
+ * row: number, // spreadsheet row (1-based, header = 1)
334
+ * email: string,
335
+ * status: 'created' | 'existing' | 'skipped',
336
+ * userId: string | null,
337
+ * message: string | null // snake_case code, optionally `code:detail`
338
+ * }>
339
+ * }
340
+ * @public
341
+ * @async
342
+ * @example
343
+ *
344
+ * const API = require('@docbrasil/api-systemmanager');
345
+ * const api = new API();
346
+ * const fd = new FormData();
347
+ * fd.append('file', fileInput.files[0]); // .xlsx or .csv
348
+ * const session = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
349
+ * // Ensure the client is scoped to the caller's org:
350
+ * api.admin.user.setOrgId(myOrgId);
351
+ * try {
352
+ * const result = await api.admin.user.batchCreate(fd, session);
353
+ * console.log(`${result.created} created, ${result.skipped} skipped`);
354
+ * } catch (ex) {
355
+ * if (ex?.response?.status === 422 && ex.response.data?.results) {
356
+ * // All-skipped batch — still a valid result to render.
357
+ * console.warn('All rows skipped:', ex.response.data.results);
358
+ * } else {
359
+ * throw ex;
360
+ * }
361
+ * }
362
+ */
363
+ async batchCreate(formData, session) {
364
+ const self = this;
365
+
366
+ try {
367
+ Joi.assert(formData, Joi.any().required(), 'Multipart FormData with a `file` field');
368
+ Joi.assert(session, Joi.string().required(), 'Session token');
369
+
370
+ // Do NOT force Content-Type — let the browser/axios set it with the
371
+ // correct multipart boundary. Raise the axios body-size caps to 5 MB
372
+ // (server enforces its own 2 MB cap via Hapi `maxBytes`).
373
+ const cfg = {
374
+ ...self._setHeader(session),
375
+ maxContentLength: 5 * 1024 * 1024,
376
+ maxBodyLength: 5 * 1024 * 1024
377
+ };
378
+ const apiCall = self.client.put(`${self._basePath()}/batch`, formData, cfg);
379
+ return self._returnData(await apiCall);
380
+ } catch (ex) {
381
+ throw ex;
382
+ }
383
+ }
384
+
301
385
  /**
302
386
  * @author Myndware <augusto.pissarra@myndware.com>
303
387
  * @description Remove a user
package/api/user/task.js CHANGED
@@ -256,6 +256,7 @@ class Task {
256
256
  * @param {string=} params.title - Task title
257
257
  * @param {array=} params.tags - Task tags
258
258
  * @param {string=} params.dueDate - Due date ISO string
259
+ * @param {object=} params.processProperties - Keys merged into the process's processProperties bag (search, task cards, BI); omitted when empty
259
260
  * @param {string} session - Session, token JWT
260
261
  * @return {Promise}
261
262
  * @public
@@ -279,7 +280,8 @@ class Task {
279
280
  * order: 0,
280
281
  * title: 'My Task',
281
282
  * tags: [],
282
- * dueDate: '2024-01-15T00:00:00Z'
283
+ * dueDate: '2024-01-15T00:00:00Z',
284
+ * processProperties: { surgicalPatientName: 'Maria' } // optional, merged into the process bag
283
285
  * };
284
286
  * const session = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
285
287
  * await api.user.task.saveTask(params, session);
@@ -304,6 +306,7 @@ class Task {
304
306
  Joi.assert(params.title, Joi.string().allow(''), 'Task title');
305
307
  Joi.assert(params.tags, Joi.array(), 'Task tags');
306
308
  Joi.assert(params.dueDate, Joi.string(), 'Due date ISO string');
309
+ Joi.assert(params.processProperties, Joi.object(), 'Process properties to merge into the process bag');
307
310
  Joi.assert(session, Joi.string().required(), 'Session token JWT');
308
311
 
309
312
  const {
@@ -321,7 +324,8 @@ class Task {
321
324
  order = 0,
322
325
  title = '',
323
326
  tags = [],
324
- dueDate
327
+ dueDate,
328
+ processProperties
325
329
  } = params;
326
330
 
327
331
  const body = {
@@ -337,6 +341,13 @@ class Task {
337
341
  tags
338
342
  };
339
343
  if (dueDate) body.dueDate = dueDate;
344
+ // Forwarded verbatim; the server MERGES each key into the process's own
345
+ // `processProperties` bag (site setStepData). Omitted when empty so a save
346
+ // that has nothing to publish carries no key at all. Only a plain object
347
+ // travels: an array or scalar would be folded into the bag index by index.
348
+ if (_.isPlainObject(processProperties) && !_.isEmpty(processProperties)) {
349
+ body.processProperties = processProperties;
350
+ }
340
351
 
341
352
  const url = `organizations/${orgId}/adhoc/${processId}/save/${taskId}/${flowName}`;
342
353
  const apiCall = self._client.put(url, body, self._setHeader(session));
@@ -366,6 +377,7 @@ class Task {
366
377
  * @param {string=} params.title - Task title
367
378
  * @param {array=} params.tags - Task tags
368
379
  * @param {string=} params.dueDate - Due date ISO string
380
+ * @param {object=} params.processProperties - Keys merged into the process's processProperties bag (search, task cards, BI); omitted when empty
369
381
  * @param {string} session - Session, token JWT
370
382
  * @return {Promise}
371
383
  * @public
@@ -389,7 +401,8 @@ class Task {
389
401
  * order: 0,
390
402
  * title: 'My Task',
391
403
  * tags: [],
392
- * dueDate: '2024-01-15T00:00:00Z'
404
+ * dueDate: '2024-01-15T00:00:00Z',
405
+ * processProperties: { surgicalPatientName: 'Maria' } // optional, merged into the process bag
393
406
  * };
394
407
  * const session = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
395
408
  * await api.user.task.endTask(params, session);
@@ -414,6 +427,7 @@ class Task {
414
427
  Joi.assert(params.title, Joi.string().allow(''), 'Task title');
415
428
  Joi.assert(params.tags, Joi.array(), 'Task tags');
416
429
  Joi.assert(params.dueDate, Joi.string(), 'Due date ISO string');
430
+ Joi.assert(params.processProperties, Joi.object(), 'Process properties to merge into the process bag');
417
431
  Joi.assert(session, Joi.string().required(), 'Session token JWT');
418
432
 
419
433
  const {
@@ -431,7 +445,8 @@ class Task {
431
445
  order = 0,
432
446
  title = '',
433
447
  tags = [],
434
- dueDate
448
+ dueDate,
449
+ processProperties
435
450
  } = params;
436
451
 
437
452
  const body = {
@@ -447,6 +462,13 @@ class Task {
447
462
  tags
448
463
  };
449
464
  if (dueDate) body.dueDate = dueDate;
465
+ // Forwarded verbatim; the server MERGES each key into the process's own
466
+ // `processProperties` bag (site setStepData). Omitted when empty so a save
467
+ // that has nothing to publish carries no key at all. Only a plain object
468
+ // travels: an array or scalar would be folded into the bag index by index.
469
+ if (_.isPlainObject(processProperties) && !_.isEmpty(processProperties)) {
470
+ body.processProperties = processProperties;
471
+ }
450
472
 
451
473
  const url = `organizations/${orgId}/adhoc/${processId}/endprocess/${taskId}/${flowName}`;
452
474
  const apiCall = self._client.put(url, body, self._setHeader(session));
package/dist/bundle.cjs CHANGED
@@ -3660,6 +3660,7 @@ class Task {
3660
3660
  * @param {string=} params.title - Task title
3661
3661
  * @param {array=} params.tags - Task tags
3662
3662
  * @param {string=} params.dueDate - Due date ISO string
3663
+ * @param {object=} params.processProperties - Keys merged into the process's processProperties bag (search, task cards, BI); omitted when empty
3663
3664
  * @param {string} session - Session, token JWT
3664
3665
  * @return {Promise}
3665
3666
  * @public
@@ -3683,7 +3684,8 @@ class Task {
3683
3684
  * order: 0,
3684
3685
  * title: 'My Task',
3685
3686
  * tags: [],
3686
- * dueDate: '2024-01-15T00:00:00Z'
3687
+ * dueDate: '2024-01-15T00:00:00Z',
3688
+ * processProperties: { surgicalPatientName: 'Maria' } // optional, merged into the process bag
3687
3689
  * };
3688
3690
  * const session = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
3689
3691
  * await api.user.task.saveTask(params, session);
@@ -3708,6 +3710,7 @@ class Task {
3708
3710
  Joi__default["default"].assert(params.title, Joi__default["default"].string().allow(''), 'Task title');
3709
3711
  Joi__default["default"].assert(params.tags, Joi__default["default"].array(), 'Task tags');
3710
3712
  Joi__default["default"].assert(params.dueDate, Joi__default["default"].string(), 'Due date ISO string');
3713
+ Joi__default["default"].assert(params.processProperties, Joi__default["default"].object(), 'Process properties to merge into the process bag');
3711
3714
  Joi__default["default"].assert(session, Joi__default["default"].string().required(), 'Session token JWT');
3712
3715
 
3713
3716
  const {
@@ -3725,7 +3728,8 @@ class Task {
3725
3728
  order = 0,
3726
3729
  title = '',
3727
3730
  tags = [],
3728
- dueDate
3731
+ dueDate,
3732
+ processProperties
3729
3733
  } = params;
3730
3734
 
3731
3735
  const body = {
@@ -3741,6 +3745,13 @@ class Task {
3741
3745
  tags
3742
3746
  };
3743
3747
  if (dueDate) body.dueDate = dueDate;
3748
+ // Forwarded verbatim; the server MERGES each key into the process's own
3749
+ // `processProperties` bag (site setStepData). Omitted when empty so a save
3750
+ // that has nothing to publish carries no key at all. Only a plain object
3751
+ // travels: an array or scalar would be folded into the bag index by index.
3752
+ if (___default["default"].isPlainObject(processProperties) && !___default["default"].isEmpty(processProperties)) {
3753
+ body.processProperties = processProperties;
3754
+ }
3744
3755
 
3745
3756
  const url = `organizations/${orgId}/adhoc/${processId}/save/${taskId}/${flowName}`;
3746
3757
  const apiCall = self._client.put(url, body, self._setHeader(session));
@@ -3770,6 +3781,7 @@ class Task {
3770
3781
  * @param {string=} params.title - Task title
3771
3782
  * @param {array=} params.tags - Task tags
3772
3783
  * @param {string=} params.dueDate - Due date ISO string
3784
+ * @param {object=} params.processProperties - Keys merged into the process's processProperties bag (search, task cards, BI); omitted when empty
3773
3785
  * @param {string} session - Session, token JWT
3774
3786
  * @return {Promise}
3775
3787
  * @public
@@ -3793,7 +3805,8 @@ class Task {
3793
3805
  * order: 0,
3794
3806
  * title: 'My Task',
3795
3807
  * tags: [],
3796
- * dueDate: '2024-01-15T00:00:00Z'
3808
+ * dueDate: '2024-01-15T00:00:00Z',
3809
+ * processProperties: { surgicalPatientName: 'Maria' } // optional, merged into the process bag
3797
3810
  * };
3798
3811
  * const session = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
3799
3812
  * await api.user.task.endTask(params, session);
@@ -3818,6 +3831,7 @@ class Task {
3818
3831
  Joi__default["default"].assert(params.title, Joi__default["default"].string().allow(''), 'Task title');
3819
3832
  Joi__default["default"].assert(params.tags, Joi__default["default"].array(), 'Task tags');
3820
3833
  Joi__default["default"].assert(params.dueDate, Joi__default["default"].string(), 'Due date ISO string');
3834
+ Joi__default["default"].assert(params.processProperties, Joi__default["default"].object(), 'Process properties to merge into the process bag');
3821
3835
  Joi__default["default"].assert(session, Joi__default["default"].string().required(), 'Session token JWT');
3822
3836
 
3823
3837
  const {
@@ -3835,7 +3849,8 @@ class Task {
3835
3849
  order = 0,
3836
3850
  title = '',
3837
3851
  tags = [],
3838
- dueDate
3852
+ dueDate,
3853
+ processProperties
3839
3854
  } = params;
3840
3855
 
3841
3856
  const body = {
@@ -3851,6 +3866,13 @@ class Task {
3851
3866
  tags
3852
3867
  };
3853
3868
  if (dueDate) body.dueDate = dueDate;
3869
+ // Forwarded verbatim; the server MERGES each key into the process's own
3870
+ // `processProperties` bag (site setStepData). Omitted when empty so a save
3871
+ // that has nothing to publish carries no key at all. Only a plain object
3872
+ // travels: an array or scalar would be folded into the bag index by index.
3873
+ if (___default["default"].isPlainObject(processProperties) && !___default["default"].isEmpty(processProperties)) {
3874
+ body.processProperties = processProperties;
3875
+ }
3854
3876
 
3855
3877
  const url = `organizations/${orgId}/adhoc/${processId}/endprocess/${taskId}/${flowName}`;
3856
3878
  const apiCall = self._client.put(url, body, self._setHeader(session));
@@ -14903,6 +14925,90 @@ class AdminUser {
14903
14925
  }
14904
14926
  }
14905
14927
 
14928
+ /**
14929
+ * @author Myndware <augusto.pissarra@myndware.com>
14930
+ * @description Batch-create users from an uploaded Excel (.xlsx) or CSV file.
14931
+ *
14932
+ * Uploads the file as multipart/form-data. The server parses it, validates
14933
+ * headers, de-duplicates emails, admits rows FIFO against the organization's
14934
+ * user cap, and delegates the actual creation to the existing registration
14935
+ * chain. Response is a per-row result array (created / existing / skipped).
14936
+ *
14937
+ * Status codes:
14938
+ * - 200 when at least one row was created or matched an existing user.
14939
+ * - 422 (same JSON body shape) when EVERY row was skipped — callers
14940
+ * should promote the 422 response body to a completed result, not an
14941
+ * error. Axios throws on 422 by default, so catch and inspect
14942
+ * `ex.response.data.results`.
14943
+ * - 400 for structural failures (invalid_file, missing_columns, empty_file,
14944
+ * too_many_rows) — `response.data.code` carries the machine-readable code.
14945
+ * - 403 when the caller does not belong to the target organization or lacks
14946
+ * user-admin role (code: 'forbidden').
14947
+ * - 413 when the uploaded file exceeds 2 MB.
14948
+ *
14949
+ * @param {FormData} formData A browser FormData instance with a single field
14950
+ * named `file` whose value is the .xlsx or .csv File/Blob. Must be
14951
+ * FormData so the browser/axios can set the multipart boundary.
14952
+ * @param {string} session JWT session token
14953
+ * @return {Promise<object>} Batch result:
14954
+ * {
14955
+ * total: number,
14956
+ * created: number,
14957
+ * existing: number,
14958
+ * skipped: number,
14959
+ * results: Array<{
14960
+ * row: number, // spreadsheet row (1-based, header = 1)
14961
+ * email: string,
14962
+ * status: 'created' | 'existing' | 'skipped',
14963
+ * userId: string | null,
14964
+ * message: string | null // snake_case code, optionally `code:detail`
14965
+ * }>
14966
+ * }
14967
+ * @public
14968
+ * @async
14969
+ * @example
14970
+ *
14971
+ * const API = require('@docbrasil/api-systemmanager');
14972
+ * const api = new API();
14973
+ * const fd = new FormData();
14974
+ * fd.append('file', fileInput.files[0]); // .xlsx or .csv
14975
+ * const session = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
14976
+ * // Ensure the client is scoped to the caller's org:
14977
+ * api.admin.user.setOrgId(myOrgId);
14978
+ * try {
14979
+ * const result = await api.admin.user.batchCreate(fd, session);
14980
+ * console.log(`${result.created} created, ${result.skipped} skipped`);
14981
+ * } catch (ex) {
14982
+ * if (ex?.response?.status === 422 && ex.response.data?.results) {
14983
+ * // All-skipped batch — still a valid result to render.
14984
+ * console.warn('All rows skipped:', ex.response.data.results);
14985
+ * } else {
14986
+ * throw ex;
14987
+ * }
14988
+ * }
14989
+ */
14990
+ async batchCreate(formData, session) {
14991
+ const self = this;
14992
+
14993
+ try {
14994
+ Joi__default["default"].assert(formData, Joi__default["default"].any().required(), 'Multipart FormData with a `file` field');
14995
+ Joi__default["default"].assert(session, Joi__default["default"].string().required(), 'Session token');
14996
+
14997
+ // Do NOT force Content-Type — let the browser/axios set it with the
14998
+ // correct multipart boundary. Raise the axios body-size caps to 5 MB
14999
+ // (server enforces its own 2 MB cap via Hapi `maxBytes`).
15000
+ const cfg = {
15001
+ ...self._setHeader(session),
15002
+ maxContentLength: 5 * 1024 * 1024,
15003
+ maxBodyLength: 5 * 1024 * 1024
15004
+ };
15005
+ const apiCall = self.client.put(`${self._basePath()}/batch`, formData, cfg);
15006
+ return self._returnData(await apiCall);
15007
+ } catch (ex) {
15008
+ throw ex;
15009
+ }
15010
+ }
15011
+
14906
15012
  /**
14907
15013
  * @author Myndware <augusto.pissarra@myndware.com>
14908
15014
  * @description Remove a user