emailengine-app 2.69.0 → 2.70.0

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/.github/workflows/deploy.yml +6 -3
  2. package/.github/workflows/release.yaml +2 -0
  3. package/CHANGELOG.md +19 -0
  4. package/Gruntfile.js +3 -1
  5. package/data/google-crawlers.json +1 -1
  6. package/getswagger.sh +40 -4
  7. package/gettext-extract.js +163 -0
  8. package/lib/account.js +73 -47
  9. package/lib/api-routes/account-routes.js +231 -71
  10. package/lib/api-routes/blocklist-routes.js +25 -18
  11. package/lib/api-routes/chat-routes.js +32 -14
  12. package/lib/api-routes/delivery-test-routes.js +30 -5
  13. package/lib/api-routes/export-routes.js +27 -2
  14. package/lib/api-routes/gateway-routes.js +63 -12
  15. package/lib/api-routes/license-routes.js +18 -4
  16. package/lib/api-routes/mailbox-routes.js +33 -7
  17. package/lib/api-routes/message-routes.js +200 -58
  18. package/lib/api-routes/oauth2-app-routes.js +90 -24
  19. package/lib/api-routes/outbox-routes.js +16 -4
  20. package/lib/api-routes/pubsub-routes.js +8 -4
  21. package/lib/api-routes/route-helpers.js +14 -1
  22. package/lib/api-routes/settings-routes.js +51 -25
  23. package/lib/api-routes/stats-routes.js +37 -3
  24. package/lib/api-routes/submit-routes.js +31 -42
  25. package/lib/api-routes/template-routes.js +54 -21
  26. package/lib/api-routes/token-routes.js +67 -67
  27. package/lib/api-routes/webhook-route-routes.js +37 -8
  28. package/lib/autodetect-imap-settings.js +0 -2
  29. package/lib/consts.js +5 -0
  30. package/lib/email-client/base-client.js +28 -6
  31. package/lib/email-client/gmail-client.js +119 -112
  32. package/lib/email-client/imap/subconnection.js +0 -1
  33. package/lib/email-client/imap/sync-operations.js +1 -1
  34. package/lib/email-client/imap-client.js +36 -17
  35. package/lib/email-client/notification-handler.js +1 -4
  36. package/lib/email-client/outlook-client.js +49 -62
  37. package/lib/export.js +37 -1
  38. package/lib/feature-flags.js +2 -2
  39. package/lib/gateway.js +4 -9
  40. package/lib/get-raw-email.js +5 -5
  41. package/lib/imapproxy/imap-core/lib/imap-connection.js +0 -1
  42. package/lib/logger.js +24 -21
  43. package/lib/metrics-collector.js +0 -2
  44. package/lib/oauth2-apps.js +13 -4
  45. package/lib/outbox.js +24 -40
  46. package/lib/redis-operations.js +1 -1
  47. package/lib/schemas.js +403 -83
  48. package/lib/sentry.js +139 -0
  49. package/lib/settings.js +9 -3
  50. package/lib/stream-encrypt.js +1 -1
  51. package/lib/templates.js +1 -1
  52. package/lib/tokens.js +5 -3
  53. package/lib/tools.js +2 -4
  54. package/lib/ui-routes/account-routes.js +7 -4
  55. package/lib/ui-routes/admin-config-routes.js +16 -3
  56. package/lib/ui-routes/oauth-config-routes.js +0 -2
  57. package/lib/ui-routes/route-helpers.js +0 -2
  58. package/lib/ui-routes/unsubscribe-routes.js +0 -2
  59. package/lib/webhooks.js +8 -4
  60. package/package.json +9 -8
  61. package/sbom.json +1 -1
  62. package/server.js +8 -23
  63. package/static/licenses.html +152 -292
  64. package/translations/messages.pot +122 -122
  65. package/update-info.sh +19 -1
  66. package/views/config/logging.hbs +48 -0
  67. package/workers/api.js +11 -32
  68. package/workers/documents.js +2 -22
  69. package/workers/export.js +16 -50
  70. package/workers/imap-proxy.js +3 -23
  71. package/workers/imap.js +2 -22
  72. package/workers/smtp.js +2 -22
  73. package/workers/submit.js +6 -24
  74. package/workers/webhooks.js +2 -22
@@ -12,17 +12,39 @@ const {
12
12
  accountIdSchema,
13
13
  messageDetailsSchema,
14
14
  messageListSchema,
15
+ messageEntrySchema,
15
16
  documentStoreSchema,
17
+ documentStoreQuerySchema,
16
18
  searchSchema,
17
19
  messageUpdateSchema,
18
20
  addressSchema,
19
21
  fromAddressSchema,
20
- messageReferenceSchema
22
+ messageReferenceSchema,
23
+ emailIdSchema,
24
+ threadIdSchema,
25
+ errorResponses
21
26
  } = require('../schemas');
22
27
 
23
28
  const listMessageFolderPathDescription =
24
29
  'Mailbox folder path. Can use special use labels like "\\Sent". Special value "\\All" is available for Gmail IMAP, Gmail API, MS Graph API accounts.';
25
30
 
31
+ // Response field for a flag or label update operation. IMAP accounts return a boolean (whether
32
+ // the operation succeeded), API-based accounts echo the requested values as an array.
33
+ const updateOpResultSchema = (op, noun, entryLabel, example, label) =>
34
+ Joi.alternatives()
35
+ .try(Joi.boolean(), Joi.array().items(Joi.string().label(entryLabel)))
36
+ .example(example)
37
+ .description(`Did the ${op} operation succeed (boolean, IMAP accounts) or the requested ${noun} (array, API-based accounts)`)
38
+ .label(label);
39
+
40
+ // Source-to-target ID map for messages that were moved to another folder. Only the IMAP backend
41
+ // provides this info.
42
+ const idMapSchema = Joi.array()
43
+ .items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
44
+ .example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
45
+ .description('An optional map of source and target ID values, if the server provided this info (IMAP accounts only)')
46
+ .label('IdMapArray');
47
+
26
48
  async function init(args) {
27
49
  const { server, call, CORS_CONFIG, MAX_ATTACHMENT_SIZE, MAX_BODY_SIZE, MAX_PAYLOAD_TIMEOUT } = args;
28
50
 
@@ -60,7 +82,8 @@ async function init(args) {
60
82
 
61
83
  plugins: {
62
84
  'hapi-swagger': {
63
- produces: ['message/rfc822']
85
+ produces: ['message/rfc822'],
86
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
64
87
  }
65
88
  },
66
89
 
@@ -106,6 +129,12 @@ async function init(args) {
106
129
  notes: 'Returns details of a specific message. By default text content is not included, use textType value to force retrieving text',
107
130
  tags: ['api', 'Message'],
108
131
 
132
+ plugins: {
133
+ 'hapi-swagger': {
134
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
135
+ }
136
+ },
137
+
109
138
  auth: {
110
139
  strategy: 'api-token',
111
140
  mode: 'required'
@@ -210,7 +239,11 @@ async function init(args) {
210
239
  notes: 'Upload a message structure, compile it into an EML file and store it into selected mailbox.',
211
240
  tags: ['api', 'Message'],
212
241
 
213
- plugins: {},
242
+ plugins: {
243
+ 'hapi-swagger': {
244
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
245
+ }
246
+ },
214
247
 
215
248
  auth: {
216
249
  strategy: 'api-token',
@@ -324,9 +357,11 @@ async function init(args) {
324
357
  )
325
358
  .label('MessageAppendId'),
326
359
  path: Joi.string().example('INBOX').description('Folder this message was uploaded to').label('MessageAppendPath'),
327
- uid: Joi.number().integer().example(12345).description('UID of uploaded message'),
328
- uidValidity: Joi.string().example('12345').description('UIDVALIDITY of the target folder. Numeric value cast as string.'),
329
- seq: Joi.number().integer().example(12345).description('Sequence number of uploaded message'),
360
+ uid: Joi.number().integer().example(12345).description('UID of uploaded message (IMAP accounts only)'),
361
+ uidValidity: Joi.string()
362
+ .example('12345')
363
+ .description('UIDVALIDITY of the target folder. Numeric value cast as string. IMAP accounts only.'),
364
+ seq: Joi.number().integer().example(12345).description('Sequence number of uploaded message (IMAP accounts only)'),
330
365
 
331
366
  messageId: Joi.string().max(996).example('<test123@example.com>').description('Message ID'),
332
367
 
@@ -334,9 +369,9 @@ async function init(args) {
334
369
  message: Joi.string()
335
370
  .base64({ paddingRequired: false, urlSafe: true })
336
371
  .max(256)
337
- .required()
338
372
  .example('AAAAAQAACnA')
339
- .description('Referenced message ID'),
373
+ .description('Referenced message ID. Not present when only a thread ID was referenced'),
374
+ threadId: threadIdSchema.description('Referenced thread ID (Gmail API accounts only)').label('ResponseReferenceThreadId'),
340
375
  success: Joi.boolean().example(true).description('Was the referenced message processed').label('ResponseReferenceSuccess'),
341
376
  documentStore: documentStoreSchema.default(false),
342
377
  error: Joi.string().example('Referenced message was not found').description('An error message if referenced message processing failed')
@@ -374,7 +409,11 @@ async function init(args) {
374
409
  notes: 'Update message information. Mainly this means changing message flag values',
375
410
  tags: ['api', 'Message'],
376
411
 
377
- plugins: {},
412
+ plugins: {
413
+ 'hapi-swagger': {
414
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
415
+ }
416
+ },
378
417
 
379
418
  auth: {
380
419
  strategy: 'api-token',
@@ -400,14 +439,24 @@ async function init(args) {
400
439
  response: {
401
440
  schema: Joi.object({
402
441
  flags: Joi.object({
403
- add: Joi.array().items(Joi.string().label('FlagEntry')).example(['\\Seen', '\\Flagged']).label('FlagAddList'),
404
- delete: Joi.array().items(Joi.string().label('FlagEntry')).example(['\\Draft']).label('FlagDeleteList'),
405
- set: Joi.array().items(Joi.string().label('FlagEntry')).example(['\\Seen']).label('FlagSetList')
442
+ add: updateOpResultSchema('add', 'flags', 'FlagEntry', true, 'FlagAddResult'),
443
+ delete: updateOpResultSchema('delete', 'flags', 'FlagEntry', false, 'FlagDeleteResult'),
444
+ set: updateOpResultSchema('set', 'flags', 'FlagEntry', true, 'FlagSetResult'),
445
+ result: Joi.array()
446
+ .items(Joi.string().label('FlagEntry'))
447
+ .example(['\\Seen'])
448
+ .description('Resulting flag set after the update (Gmail API and MS Graph API accounts only)')
449
+ .label('FlagResultList')
406
450
  }).label('FlagUpdateResponse'),
407
451
  labels: Joi.object({
408
- add: Joi.array().items(Joi.string().label('LabelEntry')).example(['Label1', 'Label2']).label('LabelAddList'),
409
- delete: Joi.array().items(Joi.string().label('LabelEntry')).example(['Label3']).label('LabelDeleteList'),
410
- set: Joi.array().items(Joi.string().label('LabelEntry')).example(['Label1']).label('LabelSetList')
452
+ add: updateOpResultSchema('add', 'labels', 'LabelEntry', ['Label1', 'Label2'], 'LabelAddResult'),
453
+ delete: updateOpResultSchema('delete', 'labels', 'LabelEntry', ['Label3'], 'LabelDeleteResult'),
454
+ set: updateOpResultSchema('set', 'labels', 'LabelEntry', ['Label1'], 'LabelSetResult'),
455
+ result: Joi.array()
456
+ .items(Joi.string().label('LabelEntry'))
457
+ .example(['Label1'])
458
+ .description('Resulting label set after the update (Gmail API and MS Graph API accounts only)')
459
+ .label('LabelResultList')
411
460
  }).label('LabelUpdateResponse')
412
461
  }).label('MessageUpdateResponse'),
413
462
  failAction: 'log'
@@ -440,7 +489,11 @@ async function init(args) {
440
489
  notes: 'Update message information for matching emails',
441
490
  tags: ['api', 'Multi Message Actions'],
442
491
 
443
- plugins: {},
492
+ plugins: {
493
+ 'hapi-swagger': {
494
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
495
+ }
496
+ },
444
497
 
445
498
  auth: {
446
499
  strategy: 'api-token',
@@ -472,15 +525,19 @@ async function init(args) {
472
525
  response: {
473
526
  schema: Joi.object({
474
527
  flags: Joi.object({
475
- add: Joi.array().items(Joi.string().label('BulkFlagEntry')).example(['\\Seen', '\\Flagged']).label('BulkFlagAddList'),
476
- delete: Joi.array().items(Joi.string().label('BulkFlagEntry')).example(['\\Draft']).label('BulkFlagDeleteList'),
477
- set: Joi.array().items(Joi.string().label('BulkFlagEntry')).example(['\\Seen']).label('BulkFlagSetList')
528
+ add: updateOpResultSchema('add', 'flags', 'BulkFlagEntry', true, 'BulkFlagAddResult'),
529
+ delete: updateOpResultSchema('delete', 'flags', 'BulkFlagEntry', false, 'BulkFlagDeleteResult'),
530
+ set: updateOpResultSchema('set', 'flags', 'BulkFlagEntry', true, 'BulkFlagSetResult')
478
531
  }).label('BulkFlagUpdateResponse'),
479
532
  labels: Joi.object({
480
- add: Joi.array().items(Joi.string().label('BulkLabelEntry')).example(['Label1', 'Label2']).label('BulkLabelAddList'),
481
- delete: Joi.array().items(Joi.string().label('BulkLabelEntry')).example(['Label3']).label('BulkLabelDeleteList'),
482
- set: Joi.array().items(Joi.string().label('BulkLabelEntry')).example(['Label1']).label('BulkLabelSetList')
483
- }).label('BulkLabelUpdateResponse')
533
+ add: updateOpResultSchema('add', 'labels', 'BulkLabelEntry', ['Label1', 'Label2'], 'BulkLabelAddResult'),
534
+ delete: updateOpResultSchema('delete', 'labels', 'BulkLabelEntry', ['Label3'], 'BulkLabelDeleteResult'),
535
+ set: updateOpResultSchema('set', 'labels', 'BulkLabelEntry', ['Label1'], 'BulkLabelSetResult')
536
+ }).label('BulkLabelUpdateResponse'),
537
+ emailIds: Joi.array()
538
+ .items(emailIdSchema)
539
+ .description('List of updated email IDs (Gmail API and MS Graph API accounts only)')
540
+ .label('BulkUpdatedEmailIds')
484
541
  }).label('BulkMessageUpdateResponse'),
485
542
  failAction: 'log'
486
543
  }
@@ -516,7 +573,11 @@ async function init(args) {
516
573
  notes: 'Moves a message to a target folder',
517
574
  tags: ['api', 'Message'],
518
575
 
519
- plugins: {},
576
+ plugins: {
577
+ 'hapi-swagger': {
578
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
579
+ }
580
+ },
520
581
 
521
582
  auth: {
522
583
  strategy: 'api-token',
@@ -586,7 +647,11 @@ async function init(args) {
586
647
  notes: 'Move messages matching to a search query to another folder',
587
648
  tags: ['api', 'Multi Message Actions'],
588
649
 
589
- plugins: {},
650
+ plugins: {
651
+ 'hapi-swagger': {
652
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
653
+ }
654
+ },
590
655
 
591
656
  auth: {
592
657
  strategy: 'api-token',
@@ -620,16 +685,15 @@ async function init(args) {
620
685
  schema: Joi.object({
621
686
  path: Joi.string().required().example('INBOX').description('Target mailbox folder path'),
622
687
 
623
- idMap: Joi.array()
624
- .items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
625
- .example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
626
- .description('An optional map of source and target ID values, if the server provided this info')
627
- .label('IdMapArray'),
688
+ idMap: idMapSchema,
628
689
 
629
690
  emailIds: Joi.array()
630
- .items(Joi.string().example('1278455344230334865'))
631
- .description('An optional list of emailId values, if the server supports unique email IDs')
632
- .label('EmailIdsArray')
691
+ .items(emailIdSchema)
692
+ .allow(null)
693
+ .description(
694
+ 'An optional list of emailId values, if the server supports unique email IDs (Gmail API and MS Graph API accounts only). Null when no messages matched the search (Gmail API)'
695
+ )
696
+ .label('MovedEmailIdsArray')
633
697
  }).label('MessagesMoveResponse'),
634
698
  failAction: 'log'
635
699
  }
@@ -661,7 +725,11 @@ async function init(args) {
661
725
  notes: 'Move message to Trash or delete it if already in Trash',
662
726
  tags: ['api', 'Message'],
663
727
 
664
- plugins: {},
728
+ plugins: {
729
+ 'hapi-swagger': {
730
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
731
+ }
732
+ },
665
733
 
666
734
  auth: {
667
735
  strategy: 'api-token',
@@ -693,10 +761,20 @@ async function init(args) {
693
761
  },
694
762
  response: {
695
763
  schema: Joi.object({
696
- deleted: Joi.boolean().example(false).description('Was the delete action executed'),
764
+ deleted: Joi.boolean()
765
+ .example(false)
766
+ .description(
767
+ 'Was the message deleted permanently. IMAP accounts return false when the message was moved to Trash, Gmail API and MS Graph API accounts return true also for moves to Trash'
768
+ ),
697
769
  moved: Joi.object({
698
- destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
699
- message: Joi.string().required().example('AAAAAwAAAWg').description('Message ID in Trash').label('TrashMessageId')
770
+ destination: Joi.string()
771
+ .example('Trash')
772
+ .description('Trash folder path. Can be missing if the server did not provide the destination folder')
773
+ .label('TrashPath'),
774
+ message: Joi.string()
775
+ .example('AAAAAwAAAWg')
776
+ .description('Message ID in Trash. Can be missing if the server did not provide the new message ID')
777
+ .label('TrashMessageId')
700
778
  })
701
779
  .description('Present if message was moved to Trash')
702
780
  .label('MessageMovedToTrash')
@@ -731,7 +809,11 @@ async function init(args) {
731
809
  notes: 'Move messages to Trash or delete these if already in Trash',
732
810
  tags: ['api', 'Multi Message Actions'],
733
811
 
734
- plugins: {},
812
+ plugins: {
813
+ 'hapi-swagger': {
814
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
815
+ }
816
+ },
735
817
 
736
818
  auth: {
737
819
  strategy: 'api-token',
@@ -757,7 +839,7 @@ async function init(args) {
757
839
  .truthy('Y', 'true', '1')
758
840
  .falsy('N', 'false', 0)
759
841
  .default(false)
760
- .description('Delete messages even if not in Trash')
842
+ .description('Delete messages even if not in Trash. Not supported for Gmail API accounts (messages are always moved to Trash)')
761
843
  .label('ForceDelete')
762
844
  }).label('MessagesDeleteQuery'),
763
845
 
@@ -768,23 +850,33 @@ async function init(args) {
768
850
 
769
851
  response: {
770
852
  schema: Joi.object({
771
- deleted: Joi.boolean().example(false).description('Was the delete action executed'),
853
+ deleted: Joi.boolean()
854
+ .example(false)
855
+ .description(
856
+ 'Was the delete action executed. IMAP accounts return false when messages were moved to Trash, Gmail API and MS Graph API accounts return true also for moves to Trash'
857
+ ),
772
858
  moved: Joi.object({
773
859
  destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
774
860
 
775
- idMap: Joi.array()
776
- .items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
777
- .example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
778
- .description('An optional map of source and target ID values, if the server provided this info')
779
- .label('IdMapArray'),
861
+ idMap: idMapSchema,
780
862
 
781
863
  emailIds: Joi.array()
782
- .items(Joi.string().example('1278455344230334865'))
783
- .description('An optional list of emailId values, if the server supports unique email IDs')
784
- .label('EmailIdsArray')
864
+ .items(emailIdSchema)
865
+ .description(
866
+ 'An optional list of emailId values, if the server supports unique email IDs (Gmail API and MS Graph API accounts only)'
867
+ )
868
+ .label('TrashedEmailIdsArray')
785
869
  })
786
870
  .label('MessagesMovedToTrash')
787
- .description('Value is present if messages were moved to Trash')
871
+ .description('Value is present if messages were moved to Trash'),
872
+ deletedMessages: Joi.object({
873
+ emailIds: Joi.array()
874
+ .items(emailIdSchema)
875
+ .description('List of emailId values of the permanently deleted messages')
876
+ .label('DeletedEmailIdsArray')
877
+ })
878
+ .label('MessagesDeleted')
879
+ .description('Value is present if messages were deleted permanently (MS Graph API accounts only)')
788
880
  }).label('MessagesDeleteResponse'),
789
881
  failAction: 'log'
790
882
  }
@@ -817,6 +909,12 @@ async function init(args) {
817
909
  notes: 'Lists messages in a mailbox folder',
818
910
  tags: ['api', 'Message'],
819
911
 
912
+ plugins: {
913
+ 'hapi-swagger': {
914
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
915
+ }
916
+ },
917
+
820
918
  auth: {
821
919
  strategy: 'api-token',
822
920
  mode: 'required'
@@ -916,7 +1014,11 @@ async function init(args) {
916
1014
  notes: 'Filter messages from a mailbox folder by search options. Search is performed against a specific folder and not for the entire account.',
917
1015
  tags: ['api', 'Message'],
918
1016
 
919
- plugins: {},
1017
+ plugins: {
1018
+ 'hapi-swagger': {
1019
+ responses: errorResponses(400, 401, 403, 404, 422, 429, 500, 503)
1020
+ }
1021
+ },
920
1022
 
921
1023
  auth: {
922
1024
  strategy: 'api-token',
@@ -980,7 +1082,7 @@ async function init(args) {
980
1082
  exposeQuery: Joi.boolean()
981
1083
  .truthy('Y', 'true', '1')
982
1084
  .falsy('N', 'false', 0)
983
- .description('If enabled then returns the ElasticSearch query for debugging as part of the response')
1085
+ .description('If enabled then includes the combined query (as the documentStoreQuery field) in the response for debugging')
984
1086
  .label('exposeQuery')
985
1087
  .when('documentStore', {
986
1088
  is: true,
@@ -1074,9 +1176,13 @@ async function init(args) {
1074
1176
  options: {
1075
1177
  description: 'Unified search for messages',
1076
1178
  notes: 'Filter messages from the Document Store for multiple accounts or paths. Document Store must be enabled for the unified search to work.',
1077
- tags: ['Deprecated endpoints (Document Store)'],
1179
+ tags: ['api', 'Deprecated endpoints (Document Store)'],
1078
1180
 
1079
- plugins: {},
1181
+ plugins: {
1182
+ 'hapi-swagger': {
1183
+ responses: errorResponses(400, 401, 403, 429, 500)
1184
+ }
1185
+ },
1080
1186
 
1081
1187
  auth: {
1082
1188
  strategy: 'api-token',
@@ -1104,7 +1210,7 @@ async function init(args) {
1104
1210
  exposeQuery: Joi.boolean()
1105
1211
  .truthy('Y', 'true', '1')
1106
1212
  .falsy('N', 'false', 0)
1107
- .description('If enabled then returns the ElasticSearch query for debugging as part of the response')
1213
+ .description('If enabled then includes the combined query (as the documentStoreQuery field) in the response for debugging')
1108
1214
  .label('exposeQuery')
1109
1215
  .optional()
1110
1216
  .meta({ swaggerHidden: true })
@@ -1127,7 +1233,34 @@ async function init(args) {
1127
1233
  },
1128
1234
 
1129
1235
  response: {
1130
- schema: messageListSchema,
1236
+ schema: Joi.object({
1237
+ total: Joi.number()
1238
+ .integer()
1239
+ .example(120)
1240
+ .description('Total number of matching messages (capped at 10000 by the Document Store)')
1241
+ .label('UnifiedTotalNumber'),
1242
+ page: Joi.number().integer().example(0).description('Current page number (zero-based)').label('UnifiedPageNumber'),
1243
+ pages: Joi.number().integer().example(24).description('Total number of pages available').label('UnifiedPagesNumber'),
1244
+ accounts: Joi.array()
1245
+ .items(Joi.string().example('example'))
1246
+ .description('Account filter used for the search, if provided')
1247
+ .label('UnifiedSearchAccountsEcho'),
1248
+ paths: Joi.array()
1249
+ .items(Joi.string().example('INBOX'))
1250
+ .description('Path filter used for the search, if provided')
1251
+ .label('UnifiedSearchPathsEcho'),
1252
+ messages: Joi.array()
1253
+ .items(
1254
+ messageEntrySchema
1255
+ .keys({
1256
+ account: accountIdSchema.description('Account ID this message belongs to')
1257
+ })
1258
+ .unknown()
1259
+ .label('UnifiedMessageListEntry')
1260
+ )
1261
+ .label('UnifiedPageMessages'),
1262
+ documentStoreQuery: documentStoreQuerySchema
1263
+ }).label('UnifiedSearchResponse'),
1131
1264
  failAction: 'log'
1132
1265
  }
1133
1266
  }
@@ -1159,6 +1292,12 @@ async function init(args) {
1159
1292
  notes: 'Retrieves message text',
1160
1293
  tags: ['api', 'Message'],
1161
1294
 
1295
+ plugins: {
1296
+ 'hapi-swagger': {
1297
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
1298
+ }
1299
+ },
1300
+
1162
1301
  auth: {
1163
1302
  strategy: 'api-token',
1164
1303
  mode: 'required'
@@ -1179,7 +1318,7 @@ async function init(args) {
1179
1318
  .min(0)
1180
1319
  .max(1024 * 1024 * 1024)
1181
1320
  .example(MAX_ATTACHMENT_SIZE)
1182
- .description('Max length of text content'),
1321
+ .description('Max length of text content. Ignored for Gmail API and MS Graph API accounts (full content is always returned)'),
1183
1322
  textType: Joi.string()
1184
1323
  .lowercase()
1185
1324
  .valid('html', 'plain', '*')
@@ -1205,7 +1344,9 @@ async function init(args) {
1205
1344
  schema: Joi.object({
1206
1345
  plain: Joi.string().example('Hello world').description('Plaintext content'),
1207
1346
  html: Joi.string().example('<p>Hello world</p>').description('HTML content'),
1208
- hasMore: Joi.boolean().example(false).description('Is the current text output capped or not')
1347
+ hasMore: Joi.boolean()
1348
+ .example(false)
1349
+ .description('Is the current text output capped or not. Always false for Gmail API and MS Graph API accounts')
1209
1350
  }).label('TextResponse'),
1210
1351
  failAction: 'log'
1211
1352
  }
@@ -1245,7 +1386,8 @@ async function init(args) {
1245
1386
 
1246
1387
  plugins: {
1247
1388
  'hapi-swagger': {
1248
- produces: ['application/octet-stream']
1389
+ produces: ['application/octet-stream'],
1390
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
1249
1391
  }
1250
1392
  },
1251
1393