@cumulus/es-client 13.4.0 → 14.1.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.
package/README.md CHANGED
@@ -8,6 +8,13 @@ Utilities for working with Elasticsearch.
8
8
  npm install @cumulus/es-client
9
9
  ```
10
10
 
11
+ ## Notes
12
+
13
+ ### Sorting
14
+
15
+ It is possible to sort the Elastic Search results by specifying `sort_by`, `sort_key`, and `order` params.
16
+ If not provided, the default sorting will be applied (`{ timestamp: { order: 'desc' } }` as of this writing).
17
+
11
18
  ## About Cumulus
12
19
 
13
20
  Cumulus is a cloud-based data ingest, archive, distribution and management prototype for NASA's
package/esDefaults.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const defaultESScrollSize = 1000;
2
2
  const defaultESScrollDuration = '6m';
3
3
 
4
- const defaultSortParams = [{ timestamp: 'desc' }, { _uid: 'asc' }];
4
+ const defaultSortParams = [{ timestamp: { order: 'desc' } }];
5
5
 
6
6
  module.exports = {
7
7
  defaultESScrollSize,
package/indexer.js CHANGED
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  /* functions for transforming and indexing Cumulus Payloads
2
4
  * in Elasticsearch. These functions are specifically designed
3
5
  * to transform data for use in cumulus api
@@ -11,11 +13,13 @@
11
13
  'use strict';
12
14
 
13
15
  const cloneDeep = require('lodash/cloneDeep');
16
+ const isEqual = require('lodash/isEqual');
14
17
 
15
18
  const Logger = require('@cumulus/logger');
16
19
  const { inTestMode } = require('@cumulus/common/test-utils');
17
- const { IndexExistsError } = require('@cumulus/errors');
20
+ const { IndexExistsError, ValidationError } = require('@cumulus/errors');
18
21
  const { constructCollectionId } = require('@cumulus/message/Collections');
22
+ const { removeNilProperties } = require('@cumulus/common/util');
19
23
 
20
24
  const { Search, defaultIndexAlias } = require('./search');
21
25
  const mappings = require('./config/mappings.json');
@@ -284,15 +288,27 @@ async function indexGranule(esClient, payload, index = defaultIndexAlias, type =
284
288
  );
285
289
  }
286
290
 
291
+ const granuleInvalidNullFields = [
292
+ 'granuleId',
293
+ 'collectionId',
294
+ 'status',
295
+ 'updatedAt',
296
+ 'execution',
297
+ 'createdAt',
298
+ ];
299
+
287
300
  /**
288
301
  * Upserts a granule in Elasticsearch
289
302
  *
290
303
  * @param {Object} params
291
- * @param {Object} params.esClient - Elasticsearch Connection object
292
- * @param {Object} params.updates - Updates to make
293
- * @param {string} params.index - Elasticsearch index alias (default defined in search.js)
294
- * @param {string} params.type - Elasticsearch type (default: granule)
295
- * @param {string} [params.refresh] - whether to refresh the index on update or not
304
+ * @param {Object} params.esClient - Elasticsearch Connection object
305
+ * @param {Object} params.updates - Updates to make
306
+ * @param {string | undefined} params.index - Elasticsearch index alias
307
+ * (default defined in search.js)
308
+ * @param {string} [params.type] - Elasticsearch type (default: granule)
309
+ * @param {string} [params.refresh] - whether to refresh the index on update or not
310
+ * @param {boolean} writeConstraints - boolean toggle restricting if conditionals should
311
+ * be used to determine write eligibility
296
312
  * @returns {Promise} Elasticsearch response
297
313
  */
298
314
  async function upsertGranule({
@@ -301,7 +317,12 @@ async function upsertGranule({
301
317
  index = defaultIndexAlias,
302
318
  type = 'granule',
303
319
  refresh,
304
- }) {
320
+ }, writeConstraints = true) {
321
+ Object.keys(updates).forEach((key) => {
322
+ if (updates[key] === null && granuleInvalidNullFields.includes(key)) {
323
+ throw new ValidationError(`Attempted DynamoDb write with invalid key ${key} set to null. Please remove or change this field and retry`);
324
+ }
325
+ });
305
326
  // If the granule exists in 'deletedgranule', delete it first before inserting the granule
306
327
  // into ES. Ignore 404 error, so the deletion still succeeds if the record doesn't exist.
307
328
  const delGranParams = {
@@ -313,7 +334,50 @@ async function upsertGranule({
313
334
  };
314
335
  await esClient.delete(delGranParams, { ignore: [404] });
315
336
 
316
- const upsertDoc = updates;
337
+ // Remove nils in case there isn't a collision
338
+ const upsertDoc = removeNilProperties(updates);
339
+ let removeString = '';
340
+
341
+ // Set field removal for null values
342
+ Object.entries(updates).forEach(([fieldName, value]) => {
343
+ // File removal is a special case as null gets set to []
344
+ if (fieldName === 'files' && isEqual(value, [])) {
345
+ removeString += `ctx._source.remove('${fieldName}'); `;
346
+ delete upsertDoc.files; // Remove files in case this is not a scripted upsert
347
+ }
348
+ if (value === null) {
349
+ removeString += `ctx._source.remove('${fieldName}'); `;
350
+ }
351
+ });
352
+
353
+ let inlineDocWriteString = 'ctx._source.putAll(params.doc);';
354
+ if (removeString !== '') {
355
+ inlineDocWriteString += removeString;
356
+ }
357
+ let inline = inlineDocWriteString;
358
+
359
+ if (writeConstraints === true) {
360
+ // Because both API write and message write chains use the granule model to store records, in
361
+ // cases where createdAt does not exist on the granule, we assume overwrite protections are
362
+ // undesired behavior via business logic on the message write logic
363
+ inline = `
364
+ if ((ctx._source.createdAt === null || params.doc.createdAt >= ctx._source.createdAt)
365
+ && ((params.doc.status != 'running' && params.doc.status != 'queued') || ((params.doc.status == 'running' || params.doc.status == 'queued') && params.doc.execution != ctx._source.execution))) {
366
+ ${inlineDocWriteString}
367
+ } else {
368
+ ctx.op = 'none';
369
+ }
370
+ `;
371
+ if (!updates.createdAt) {
372
+ inline = `
373
+ if ((params.doc.status != 'running' && params.doc.status != 'queued') || ((params.doc.status == 'running' || params.doc.status == 'queued') && params.doc.execution != ctx._source.execution)) {
374
+ ${inlineDocWriteString}
375
+ } else {
376
+ ctx.op = 'none';
377
+ }
378
+ `;
379
+ }
380
+ }
317
381
 
318
382
  return await esClient.update({
319
383
  index,
@@ -323,14 +387,7 @@ async function upsertGranule({
323
387
  body: {
324
388
  script: {
325
389
  lang: 'painless',
326
- inline: `
327
- if ((ctx._source.createdAt === null || params.doc.createdAt >= ctx._source.createdAt)
328
- && (params.doc.status != 'running' || (params.doc.status == 'running' && params.doc.execution != ctx._source.execution))) {
329
- ctx._source.putAll(params.doc);
330
- } else {
331
- ctx.op = 'none';
332
- }
333
- `,
390
+ inline,
334
391
  params: {
335
392
  doc: upsertDoc,
336
393
  },
@@ -709,4 +766,5 @@ module.exports = {
709
766
  deleteGranule,
710
767
  deleteExecution,
711
768
  deleteReconciliationReport,
769
+ granuleInvalidNullFields,
712
770
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cumulus/es-client",
3
- "version": "13.4.0",
3
+ "version": "14.1.0",
4
4
  "description": "Utilities for working with Elasticsearch",
5
5
  "keywords": [
6
6
  "CUMULUS",
@@ -30,10 +30,10 @@
30
30
  "author": "Cumulus Authors",
31
31
  "license": "Apache-2.0",
32
32
  "dependencies": {
33
- "@cumulus/common": "13.4.0",
34
- "@cumulus/errors": "13.4.0",
35
- "@cumulus/logger": "13.4.0",
36
- "@cumulus/message": "13.4.0",
33
+ "@cumulus/common": "14.1.0",
34
+ "@cumulus/errors": "14.1.0",
35
+ "@cumulus/logger": "14.1.0",
36
+ "@cumulus/message": "14.1.0",
37
37
  "@elastic/elasticsearch": "^5.6.20",
38
38
  "aws-elasticsearch-connector": "8.2.0",
39
39
  "aws-sdk": "^2.585.0",
@@ -42,9 +42,9 @@
42
42
  "p-limit": "^1.2.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@cumulus/aws-client": "13.4.0",
46
- "@cumulus/test-data": "13.4.0",
45
+ "@cumulus/aws-client": "14.1.0",
46
+ "@cumulus/test-data": "14.1.0",
47
47
  "p-each-series": "^2.1.0"
48
48
  },
49
- "gitHead": "cd4377ccf97a1daba19775fb95f9f7fa0f2a29f3"
49
+ "gitHead": "d97b5b37913944c0f0ecf958f2a567ec3714816c"
50
50
  }