kuzzle 2.16.11 → 2.17.2

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 (187) hide show
  1. package/lib/api/controllers/adminController.js +3 -3
  2. package/lib/api/controllers/authController.js +12 -12
  3. package/lib/api/controllers/baseController.js +60 -3
  4. package/lib/api/controllers/clusterController.js +1 -1
  5. package/lib/api/controllers/collectionController.js +7 -5
  6. package/lib/api/controllers/documentController.js +130 -17
  7. package/lib/api/controllers/indexController.js +1 -1
  8. package/lib/api/controllers/memoryStorageController.js +39 -38
  9. package/lib/api/controllers/realtimeController.js +1 -1
  10. package/lib/api/controllers/securityController.js +50 -50
  11. package/lib/api/controllers/serverController.js +73 -27
  12. package/lib/api/documentExtractor.js +3 -3
  13. package/lib/api/funnel.js +44 -21
  14. package/lib/api/httpRoutes.js +9 -4
  15. package/lib/api/openapi/OpenApiManager.d.ts +11 -0
  16. package/lib/api/openapi/OpenApiManager.js +96 -0
  17. package/lib/api/openapi/{document → components/document}/count.yaml +2 -2
  18. package/lib/api/openapi/{document → components/document}/create.yaml +2 -2
  19. package/lib/api/openapi/{document → components/document}/createOrReplace.yaml +2 -2
  20. package/lib/api/openapi/{document → components/document}/delete.yaml +1 -1
  21. package/lib/api/openapi/{document → components/document}/deleteByQuery.yaml +2 -2
  22. package/lib/api/openapi/{document → components/document}/exists.yaml +1 -1
  23. package/lib/api/openapi/{document → components/document}/get.yaml +1 -1
  24. package/lib/api/openapi/{document → components/document}/index.d.ts +2 -0
  25. package/lib/api/openapi/{document → components/document}/index.js +7 -2
  26. package/lib/api/openapi/{document → components/document}/replace.yaml +2 -2
  27. package/lib/api/openapi/{document → components/document}/scroll.yaml +1 -1
  28. package/lib/api/openapi/{document → components/document}/update.yaml +2 -2
  29. package/lib/api/openapi/components/document/validate.yaml +42 -0
  30. package/lib/api/openapi/components/index.d.ts +2 -0
  31. package/lib/api/openapi/components/index.js +18 -0
  32. package/lib/api/openapi/{payloads.yaml → components/payloads.yaml} +0 -0
  33. package/lib/api/openapi/index.d.ts +1 -2
  34. package/lib/api/openapi/index.js +1 -5
  35. package/lib/api/openapi/openApiGenerator.d.ts +7 -0
  36. package/lib/api/openapi/openApiGenerator.js +133 -0
  37. package/lib/api/request/kuzzleRequest.js +8 -6
  38. package/lib/cluster/node.js +9 -9
  39. package/lib/cluster/publisher.js +1 -1
  40. package/lib/cluster/state.js +20 -4
  41. package/lib/cluster/subscriber.js +1 -1
  42. package/lib/cluster/workers/IDCardRenewer.js +2 -2
  43. package/lib/config/default.config.js +1 -0
  44. package/lib/config/index.js +6 -6
  45. package/lib/core/auth/passportResponse.js +6 -6
  46. package/lib/core/auth/passportWrapper.js +5 -5
  47. package/lib/core/backend/backend.d.ts +11 -3
  48. package/lib/core/backend/backend.js +22 -17
  49. package/lib/core/backend/backendConfig.d.ts +5 -1
  50. package/lib/core/backend/backendConfig.js +25 -2
  51. package/lib/core/backend/backendController.js +21 -5
  52. package/lib/core/backend/backendErrors.d.ts +58 -0
  53. package/lib/core/backend/backendErrors.js +121 -0
  54. package/lib/core/backend/backendHook.js +21 -5
  55. package/lib/core/backend/backendImport.js +21 -5
  56. package/lib/core/backend/backendOpenApi.d.ts +9 -0
  57. package/lib/core/backend/backendOpenApi.js +69 -0
  58. package/lib/core/backend/backendPipe.js +21 -5
  59. package/lib/core/backend/backendPlugin.js +22 -3
  60. package/lib/core/backend/backendVault.js +21 -2
  61. package/lib/core/backend/index.d.ts +2 -0
  62. package/lib/core/backend/index.js +2 -0
  63. package/lib/core/network/accessLogger.js +6 -6
  64. package/lib/core/network/clientConnection.js +1 -1
  65. package/lib/core/network/entryPoint.js +5 -5
  66. package/lib/core/network/httpRouter/index.js +5 -5
  67. package/lib/core/network/httpRouter/routeHandler.js +3 -3
  68. package/lib/core/network/httpRouter/routePart.js +5 -5
  69. package/lib/core/network/protocolManifest.js +1 -1
  70. package/lib/core/network/protocols/httpMessage.js +2 -2
  71. package/lib/core/network/protocols/httpwsProtocol.js +228 -50
  72. package/lib/core/network/protocols/mqttProtocol.js +3 -3
  73. package/lib/core/network/protocols/protocol.js +3 -3
  74. package/lib/core/network/router.js +7 -6
  75. package/lib/core/plugin/plugin.js +38 -64
  76. package/lib/core/plugin/pluginContext.js +22 -3
  77. package/lib/core/plugin/pluginManifest.js +3 -3
  78. package/lib/core/plugin/pluginRepository.js +5 -5
  79. package/lib/core/plugin/pluginsManager.js +29 -28
  80. package/lib/core/realtime/channel.js +20 -4
  81. package/lib/core/realtime/hotelClerk.js +24 -5
  82. package/lib/core/realtime/notification/server.js +1 -1
  83. package/lib/core/realtime/notification/user.js +1 -1
  84. package/lib/core/realtime/notifier.js +5 -5
  85. package/lib/core/security/index.js +1 -1
  86. package/lib/core/security/profileRepository.d.ts +176 -0
  87. package/lib/core/security/profileRepository.js +445 -443
  88. package/lib/core/security/roleRepository.js +16 -16
  89. package/lib/core/security/securityLoader.js +2 -2
  90. package/lib/core/security/tokenRepository.js +11 -11
  91. package/lib/core/security/userRepository.js +8 -8
  92. package/lib/core/shared/abstractManifest.js +4 -4
  93. package/lib/core/shared/repository.js +5 -5
  94. package/lib/core/shared/sdk/embeddedSdk.js +21 -2
  95. package/lib/core/shared/sdk/funnelProtocol.js +1 -1
  96. package/lib/core/shared/sdk/impersonatedSdk.js +1 -1
  97. package/lib/core/shared/store.js +30 -23
  98. package/lib/core/statistics/statistics.js +17 -17
  99. package/lib/core/storage/clientAdapter.js +45 -10
  100. package/lib/core/storage/indexCache.js +20 -4
  101. package/lib/core/validation/baseType.js +5 -5
  102. package/lib/core/validation/types/anything.js +1 -1
  103. package/lib/core/validation/types/boolean.js +2 -2
  104. package/lib/core/validation/types/date.js +9 -9
  105. package/lib/core/validation/types/email.js +5 -5
  106. package/lib/core/validation/types/enum.js +6 -6
  107. package/lib/core/validation/types/geoPoint.js +2 -2
  108. package/lib/core/validation/types/geoShape.js +28 -25
  109. package/lib/core/validation/types/integer.js +4 -4
  110. package/lib/core/validation/types/ipAddress.js +7 -6
  111. package/lib/core/validation/types/numeric.js +4 -4
  112. package/lib/core/validation/types/object.js +5 -5
  113. package/lib/core/validation/types/string.js +5 -5
  114. package/lib/core/validation/types/url.js +7 -6
  115. package/lib/core/validation/validation.js +95 -84
  116. package/lib/kerror/codes/1-services.json +12 -0
  117. package/lib/kerror/codes/2-api.json +12 -0
  118. package/lib/kerror/codes/3-network.json +12 -0
  119. package/lib/kerror/codes/4-plugin.json +6 -0
  120. package/lib/kerror/codes/index.js +11 -11
  121. package/lib/kerror/errors/multipleErrorsError.d.ts +1 -1
  122. package/lib/kerror/errors/multipleErrorsError.js +3 -3
  123. package/lib/kerror/index.d.ts +82 -0
  124. package/lib/kerror/index.js +176 -143
  125. package/lib/kuzzle/dumpGenerator.js +3 -3
  126. package/lib/kuzzle/event/kuzzleEventEmitter.js +4 -4
  127. package/lib/kuzzle/event/pipeRunner.js +1 -1
  128. package/lib/kuzzle/event/waterfall.js +6 -6
  129. package/lib/kuzzle/kuzzle.js +59 -9
  130. package/lib/kuzzle/log.js +3 -3
  131. package/lib/kuzzle/vault.js +3 -3
  132. package/lib/model/security/profile.d.ts +54 -0
  133. package/lib/model/security/profile.js +192 -232
  134. package/lib/model/security/rights.js +1 -1
  135. package/lib/model/security/role.d.ts +40 -0
  136. package/lib/model/security/role.js +174 -190
  137. package/lib/model/security/user.d.ts +29 -0
  138. package/lib/model/security/user.js +103 -52
  139. package/lib/model/storage/apiKey.js +2 -2
  140. package/lib/model/storage/baseModel.js +3 -3
  141. package/lib/service/cache/redis.js +7 -7
  142. package/lib/service/storage/elasticsearch.js +152 -90
  143. package/lib/service/storage/esWrapper.js +2 -3
  144. package/lib/types/ControllerDefinition.d.ts +3 -3
  145. package/lib/types/ControllerRights.d.ts +22 -0
  146. package/lib/types/ControllerRights.js +23 -0
  147. package/lib/types/HttpStream.d.ts +32 -0
  148. package/lib/types/HttpStream.js +70 -0
  149. package/lib/types/OpenApiDefinition.d.ts +43 -0
  150. package/lib/types/{config/StorageService/StorageServiceElasticsearchConfiguration.js → OpenApiDefinition.js} +1 -1
  151. package/lib/types/Plugin.js +20 -4
  152. package/lib/types/Policy.d.ts +25 -0
  153. package/lib/types/{InternalLogger.js → Policy.js} +2 -2
  154. package/lib/types/PolicyRestrictions.d.ts +21 -0
  155. package/lib/types/PolicyRestrictions.js +23 -0
  156. package/lib/types/Target.d.ts +15 -0
  157. package/lib/types/Target.js +23 -0
  158. package/lib/types/config/KuzzleConfiguration.d.ts +4 -0
  159. package/lib/types/config/ServicesConfiguration.d.ts +2 -2
  160. package/lib/types/config/{StorageService/StorageServiceElasticsearchConfiguration.d.ts → storageEngine/StorageEngineElasticsearchConfiguration.d.ts} +10 -3
  161. package/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.js +3 -0
  162. package/lib/types/errors/ErrorDefinition.d.ts +27 -0
  163. package/lib/types/errors/ErrorDefinition.js +3 -0
  164. package/lib/types/errors/ErrorDomains.d.ts +17 -0
  165. package/lib/types/errors/ErrorDomains.js +3 -0
  166. package/lib/types/index.d.ts +9 -1
  167. package/lib/types/index.js +9 -1
  168. package/lib/util/array.d.ts +11 -0
  169. package/lib/util/array.js +57 -0
  170. package/lib/util/assertType.js +6 -6
  171. package/lib/util/bufferedPassThrough.d.ts +76 -0
  172. package/lib/util/bufferedPassThrough.js +161 -0
  173. package/lib/util/deprecate.js +7 -5
  174. package/lib/util/didYouMean.js +1 -1
  175. package/lib/util/dump-collection.d.ts +3 -0
  176. package/lib/util/dump-collection.js +284 -0
  177. package/lib/util/extractFields.js +2 -2
  178. package/lib/util/inflector.d.ts +8 -0
  179. package/lib/util/inflector.js +16 -0
  180. package/lib/util/mutex.js +21 -2
  181. package/lib/util/requestAssertions.js +7 -7
  182. package/lib/util/wildcard.js +55 -0
  183. package/package-lock.json +535 -75
  184. package/package.json +5 -3
  185. package/lib/api/openApiGenerator.d.ts +0 -7
  186. package/lib/api/openApiGenerator.js +0 -197
  187. package/lib/types/InternalLogger.d.ts +0 -25
@@ -31,7 +31,7 @@ const { Mutex } = require('../../util/mutex');
31
31
  * @class AdminController
32
32
  */
33
33
  class AdminController extends NativeController {
34
- constructor() {
34
+ constructor () {
35
35
  super([
36
36
  'dump',
37
37
  'loadFixtures',
@@ -77,7 +77,7 @@ class AdminController extends NativeController {
77
77
  async resetSecurity () {
78
78
  const mutex = new Mutex('resetSecurity', { timeout: 0 });
79
79
 
80
- if (!await mutex.lock()) {
80
+ if (! await mutex.lock()) {
81
81
  throw kerror.get('api', 'process', 'action_locked', 'Kuzzle is already reseting roles, profiles and users.');
82
82
  }
83
83
 
@@ -111,7 +111,7 @@ class AdminController extends NativeController {
111
111
  async resetDatabase () {
112
112
  const mutex = new Mutex('resetDatabase', { timeout: 0 });
113
113
 
114
- if (!await mutex.lock()) {
114
+ if (! await mutex.lock()) {
115
115
  throw kerror.get('api', 'process', 'action_locked', 'Kuzzle is already reseting all indexes.');
116
116
  }
117
117
 
@@ -33,7 +33,7 @@ const kerror = require('../../kerror');
33
33
  const { has } = require('../../util/safeObject');
34
34
  const { NativeController } = require('./baseController');
35
35
  const formatProcessing = require('../../core/auth/formatProcessing');
36
- const User = require('../../model/security/user');
36
+ const { User } = require('../../model/security/user');
37
37
  const ApiKey = require('../../model/storage/apiKey');
38
38
 
39
39
  /**
@@ -44,7 +44,7 @@ class AuthController extends NativeController {
44
44
  * @param {Kuzzle} kuzzle
45
45
  * @constructor
46
46
  */
47
- constructor() {
47
+ constructor () {
48
48
  super([
49
49
  'checkRights',
50
50
  'checkToken',
@@ -110,7 +110,7 @@ class AuthController extends NativeController {
110
110
  async createApiKey (request) {
111
111
  const expiresIn = request.input.args.expiresIn || -1;
112
112
  const refresh = request.getRefresh('wait_for');
113
- const apiKeyId = request.getId({ ifMissing: 'ignore' });
113
+ const apiKeyId = request.getId({ ifMissing: 'generate' });
114
114
  const description = request.getBodyString('description');
115
115
 
116
116
  const user = request.context.user;
@@ -176,8 +176,8 @@ class AuthController extends NativeController {
176
176
  * @returns {Promise<object>}
177
177
  */
178
178
  async logout (request) {
179
- if ( !global.kuzzle.config.http.cookieAuthentication
180
- || !request.getBoolean('cookieAuth')
179
+ if ( ! global.kuzzle.config.http.cookieAuthentication
180
+ || ! request.getBoolean('cookieAuth')
181
181
  ) {
182
182
  this.assertIsAuthenticated(request);
183
183
  }
@@ -195,7 +195,7 @@ class AuthController extends NativeController {
195
195
  await global.kuzzle.ask(
196
196
  'core:security:token:deleteByKuid',
197
197
  request.getKuid(),
198
- {keepApiKeys: true});
198
+ { keepApiKeys: true });
199
199
  }
200
200
  else if ( request.context.token
201
201
  && request.context.token.type !== 'apiKey'
@@ -219,18 +219,18 @@ class AuthController extends NativeController {
219
219
  {
220
220
  httpOnly: true,
221
221
  path: '/',
222
- sameSite:'strict',
222
+ sameSite: 'strict',
223
223
  }
224
224
  )
225
225
  }
226
226
  });
227
227
  }
228
228
 
229
- return {acknowledged: true};
229
+ return { acknowledged: true };
230
230
  }
231
231
 
232
232
  // Used to send the Token using different ways when in cookieAuth mode. (DRY)
233
- async _sendToken(token, request) {
233
+ async _sendToken (token, request) {
234
234
  // Only if the support of Browser Cookie as Authentication Token is enabled
235
235
  // otherwise we should send a normal response because
236
236
  // even if the SDK / Browser can handle the cookie,
@@ -300,7 +300,7 @@ class AuthController extends NativeController {
300
300
  }
301
301
  passportRequest.original = request;
302
302
 
303
- if (!has(global.kuzzle.pluginsManager.strategies, strategy)) {
303
+ if (! has(global.kuzzle.pluginsManager.strategies, strategy)) {
304
304
  throw kerror.get('security', 'credentials', 'unknown_strategy', strategy);
305
305
  }
306
306
 
@@ -651,8 +651,8 @@ class AuthController extends NativeController {
651
651
  }
652
652
  }
653
653
 
654
- function wrapPluginError(error) {
655
- if (!(error instanceof KuzzleError)) {
654
+ function wrapPluginError (error) {
655
+ if (! (error instanceof KuzzleError)) {
656
656
  throw kerror.getFrom(
657
657
  error,
658
658
  'plugin',
@@ -30,11 +30,11 @@ const assertionError = kerror.wrap('api', 'assert');
30
30
 
31
31
  // Base class for all controllers
32
32
  class BaseController {
33
- constructor() {
33
+ constructor () {
34
34
  this.__actions = new Set();
35
35
  }
36
36
 
37
- get _actions() {
37
+ get _actions () {
38
38
  return this.__actions;
39
39
  }
40
40
 
@@ -57,7 +57,7 @@ class BaseController {
57
57
  }
58
58
 
59
59
  class NativeController extends BaseController {
60
- constructor(actions = []) {
60
+ constructor (actions = []) {
61
61
  super();
62
62
 
63
63
  this.ask = global.kuzzle.ask.bind(global.kuzzle);
@@ -135,6 +135,63 @@ class NativeController extends BaseController {
135
135
  }
136
136
  }
137
137
 
138
+ /**
139
+ * Throw if some target have:
140
+ * - missing properties
141
+ * - invalid types
142
+ * - unauthorized values
143
+ *
144
+ * @param {Array<{index:string, collections?: string[]}>} targets Array of targets
145
+ * @param {*} options
146
+ */
147
+ assertTargetsAreValid (targets, { allowEmptyCollections } = {}) {
148
+ for (let i = 0; i < targets.length; i++) {
149
+ const target = targets[i];
150
+
151
+ if (! target.index) {
152
+ throw kerror.get('api', 'assert', 'missing_argument', `targets[${i}].index`);
153
+ }
154
+ if (this._hasMultiTargets(target.index)) {
155
+ throw kerror.get('services', 'storage', 'invalid_target_format', `targets[${i}].index`, target.index);
156
+ }
157
+
158
+ if (! allowEmptyCollections && ! target.collections) {
159
+ throw kerror.get('api', 'assert', 'missing_argument', `targets[${i}].collections`);
160
+ }
161
+
162
+ if (target.collections && ! Array.isArray(target.collections)) {
163
+ throw kerror.get('api', 'assert', 'invalid_type', `targets[${i}].collections`, 'array');
164
+ }
165
+
166
+ if (! allowEmptyCollections && target.collections.length === 0) {
167
+ throw kerror.get('api', 'assert', 'empty_argument', `targets[${i}].collections`);
168
+ }
169
+
170
+ if (allowEmptyCollections
171
+ && (! target.collections
172
+ || target.collections.length === 0)
173
+ ) {
174
+ continue;
175
+ }
176
+
177
+ for (let j = 0; j < target.collections.length; j++) {
178
+ const collection = target.collections[j];
179
+
180
+ if (typeof collection !== 'string') {
181
+ throw kerror.get('api', 'assert', 'invalid_type', `targets[${i}].collections[${j}]`, 'string');
182
+ }
183
+
184
+ if (this._hasMultiTargets(collection)) {
185
+ throw kerror.get('services', 'storage', 'invalid_target_format', `targets[${i}].collections[${j}]`, collection);
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ _hasMultiTargets (str) {
192
+ return [',', '*', '+'].some(chr => str.includes(chr)) || str === '_all';
193
+ }
194
+
138
195
  /**
139
196
  * Throws if page size exceeed Kuzzle limits
140
197
  *
@@ -24,7 +24,7 @@
24
24
  const { NativeController } = require('./baseController');
25
25
 
26
26
  class ClusterController extends NativeController {
27
- constructor() {
27
+ constructor () {
28
28
  super([
29
29
  'status',
30
30
  ]);
@@ -29,7 +29,7 @@ const { NativeController } = require('./baseController');
29
29
  * @class CollectionController
30
30
  */
31
31
  class CollectionController extends NativeController {
32
- constructor() {
32
+ constructor () {
33
33
  super([
34
34
  'create',
35
35
  'delete',
@@ -128,7 +128,7 @@ class CollectionController extends NativeController {
128
128
  async searchSpecifications (request) {
129
129
  const { from, size, scrollTTL, searchBody } = request.getSearchParams();
130
130
 
131
- if (!isPlainObject(searchBody)) {
131
+ if (! isPlainObject(searchBody)) {
132
132
  throw kerror.get(
133
133
  'api',
134
134
  'assert',
@@ -218,7 +218,7 @@ class CollectionController extends NativeController {
218
218
  try {
219
219
  await global.kuzzle.internalIndex.delete('validations', specificationsId);
220
220
  }
221
- catch(error) {
221
+ catch (error) {
222
222
  if (error.status === 404) {
223
223
  return {
224
224
  acknowledged: true,
@@ -435,7 +435,8 @@ class CollectionController extends NativeController {
435
435
  if (from || size) {
436
436
  if (from) {
437
437
  response.from = Number.parseInt(from);
438
- } else {
438
+ }
439
+ else {
439
440
  response.from = 0;
440
441
  }
441
442
 
@@ -446,7 +447,8 @@ class CollectionController extends NativeController {
446
447
  response.from,
447
448
  response.from + response.size
448
449
  );
449
- } else {
450
+ }
451
+ else {
450
452
  response.collections = response.collections.slice(response.from);
451
453
  }
452
454
  }
@@ -29,12 +29,12 @@ const {
29
29
  assertHasIndexAndCollection,
30
30
  } = require('../../util/requestAssertions');
31
31
  const extractFields = require('../../util/extractFields');
32
-
32
+ const { dumpCollectionDocuments } = require('../../util/dump-collection');
33
33
  /**
34
34
  * @class DocumentController
35
35
  */
36
36
  class DocumentController extends NativeController {
37
- constructor() {
37
+ constructor () {
38
38
  super([
39
39
  'count',
40
40
  'create',
@@ -43,10 +43,12 @@ class DocumentController extends NativeController {
43
43
  'deleteByQuery',
44
44
  'deleteFields',
45
45
  'exists',
46
+ 'export',
46
47
  'get',
47
48
  'mCreate',
48
49
  'mCreateOrReplace',
49
50
  'mDelete',
51
+ 'mExists',
50
52
  'mGet',
51
53
  'mReplace',
52
54
  'mUpdate',
@@ -67,15 +69,31 @@ class DocumentController extends NativeController {
67
69
  */
68
70
  async search (request) {
69
71
  const { from, size, scrollTTL, searchBody } = request.getSearchParams();
70
- const { index, collection } = request.getIndexAndCollection();
72
+ const index = request.input.args.index;
73
+ const collection = request.input.args.collection;
74
+ const targets = request.getArray('targets', []);
71
75
  const lang = request.getLangParam();
72
76
 
73
- if (hasMultiTargets(index)) {
74
- throw kerror.get('services', 'storage', 'no_multi_indexes');
77
+ if ( ! index
78
+ && ! collection
79
+ && (! targets || targets.length === 0)
80
+ ) {
81
+ throw kerror.get('api', 'assert', 'missing_argument', 'index, collection or targets');
82
+ }
83
+
84
+ if ( index
85
+ && collection
86
+ && (hasMultiTargets(index)
87
+ || hasMultiTargets(collection))
88
+ ) {
89
+ throw kerror.get('services', 'storage', 'invalid_multi_index_collection_usage');
75
90
  }
76
91
 
77
- if (hasMultiTargets(collection)) {
78
- throw kerror.get('services', 'storage', 'no_multi_collections');
92
+ if (targets.length > 0) {
93
+ // Index and Collections from a target should not contains any characters for multi targetting or _all
94
+ // Otherwise it might be possible to bypass some checks and access data that should be accessed
95
+ // We should also verify that each target has an index and collections specified before further processing
96
+ this.assertTargetsAreValid(targets);
79
97
  }
80
98
 
81
99
  this.assertNotExceedMaxFetch(size - from);
@@ -84,13 +102,23 @@ class DocumentController extends NativeController {
84
102
  searchBody.query = await this.translateKoncorde(searchBody.query || {});
85
103
  }
86
104
 
87
- const result = await this.ask(
88
- 'core:storage:public:document:search',
89
- index,
90
- collection,
91
- searchBody,
92
- { from, scroll: scrollTTL, size });
93
-
105
+ let result;
106
+ if (targets.length > 0) {
107
+ result = await this.ask(
108
+ 'core:storage:public:document:multiSearch',
109
+ targets,
110
+ searchBody,
111
+ { from, scroll: scrollTTL, size });
112
+ }
113
+ else {
114
+ result = await this.ask(
115
+ 'core:storage:public:document:search',
116
+ index,
117
+ collection,
118
+ searchBody,
119
+ { from, scroll: scrollTTL, size });
120
+ }
121
+
94
122
  return {
95
123
  aggregations: result.aggregations,
96
124
  hits: result.hits,
@@ -133,6 +161,91 @@ class DocumentController extends NativeController {
133
161
  return this.ask('core:storage:public:document:exist', index, collection, id);
134
162
  }
135
163
 
164
+ /**
165
+ * @param {Request} request
166
+ */
167
+ async mExists (request) {
168
+ let ids;
169
+ if ( request.input.body
170
+ && request.input.body.ids
171
+ && Object.keys(request.input.body.ids).length
172
+ ) {
173
+ ids = request.getBodyArray('ids');
174
+ }
175
+ else {
176
+ ids = request.getArray('ids');
177
+ }
178
+ const { index, collection } = request.getIndexAndCollection();
179
+
180
+ const strict = request.getBoolean('strict');
181
+ this.assertNotExceedMaxFetch(ids.length);
182
+
183
+ const { items, errors } = await this.ask(
184
+ 'core:storage:public:document:mExists',
185
+ index, collection,
186
+ ids);
187
+
188
+ if (strict && errors.length) {
189
+ throw kerror.get(
190
+ 'api',
191
+ 'process',
192
+ 'incomplete_multiple_request',
193
+ 'get',
194
+ errors);
195
+ }
196
+
197
+ return {
198
+ errors,
199
+ successes: items
200
+ };
201
+ }
202
+
203
+ async export (request) {
204
+ const { index, collection } = request.getIndexAndCollection();
205
+ const { size, scrollTTL, searchBody } = request.getSearchParams();
206
+ const format = request.getString('format', 'jsonl');
207
+ const fields = request.getBodyArray('fields', []);
208
+ const lang = request.getLangParam();
209
+ const separator = request.getString('separator', ',');
210
+ const fieldsName = request.getBodyObject('fieldsName', {});
211
+
212
+ // Remove "fields" and "fieldsName" from searchBody to avoid ES throwing an error
213
+ // since those properties are not allowed in the searchBody
214
+ searchBody.fields = undefined;
215
+ searchBody.fieldsName = undefined;
216
+
217
+ if (request.context.connection.protocol !== 'http') {
218
+ throw kerror.get('api', 'assert', 'unsupported_protocol', request.context.connection.protocol, 'document:export');
219
+ }
220
+
221
+ if (lang === 'koncorde') {
222
+ searchBody.query = await this.translateKoncorde(searchBody.query || {});
223
+ }
224
+
225
+ let mimeType = 'html/text';
226
+
227
+ if (format.toLowerCase() === 'csv') {
228
+ mimeType = 'text/csv';
229
+ }
230
+
231
+ request.response.setHeader('Content-Type', mimeType);
232
+ request.response.setHeader('Content-Disposition', `attachment; filename="${index}-${collection}.${format}"`);
233
+
234
+ return dumpCollectionDocuments(
235
+ index,
236
+ collection,
237
+ searchBody,
238
+ format,
239
+ fields,
240
+ {
241
+ fieldsName,
242
+ lang,
243
+ scroll: scrollTTL || '5s',
244
+ separator,
245
+ size,
246
+ });
247
+ }
248
+
136
249
  /**
137
250
  * @param {Request} request
138
251
  * @returns {Promise<Object>}
@@ -226,7 +339,7 @@ class DocumentController extends NativeController {
226
339
  'core:storage:public:document:create',
227
340
  index,
228
341
  collection,
229
- request.getBody(validated),
342
+ validated.getBody(),
230
343
  { id, refresh, userId });
231
344
 
232
345
  if (! silent) {
@@ -685,7 +798,7 @@ class DocumentController extends NativeController {
685
798
  })));
686
799
  }
687
800
 
688
- if (!source) {
801
+ if (! source) {
689
802
  result.successes.forEach(d => (d._source = undefined));
690
803
  }
691
804
 
@@ -773,7 +886,7 @@ class DocumentController extends NativeController {
773
886
  request,
774
887
  action,
775
888
  response.items.map(item => {
776
- if (!item.created) {
889
+ if (! item.created) {
777
890
  item._updatedFields = extractFields(
778
891
  documentsDictionnary[item._id],
779
892
  { fieldsToIgnore: ['_kuzzle_info'] });
@@ -30,7 +30,7 @@ const { NativeController } = require('./baseController');
30
30
  * @class IndexController
31
31
  */
32
32
  class IndexController extends NativeController {
33
- constructor() {
33
+ constructor () {
34
34
  super([
35
35
  'create',
36
36
  'delete',