@dmptool/utils 1.0.28 → 1.0.30

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
@@ -15,7 +15,7 @@ See below for usage examples of each utility.
15
15
  - [AWS CloudFormation Stack Output Access](#cloudformation-support)
16
16
  - [AWS DynamoDB Table Access](#dynamodb-support)
17
17
  - [General Helper Functions](#general-helper-functions)
18
- - [EventBridge Event Publication](#eventbridge-support)
18
+ - [SQS Message Publication](#sqs-support)
19
19
  - [Logger Support (Pino with ECS formatting)](#logger-support-pino-with-ecs-formatting)
20
20
  - [maDMP Support (serialization and deserialization)](#madmp-support-serialization-and-deserialization)
21
21
  - [AWS RDS MySQL Database Access](#rds-mysql-support)
@@ -209,40 +209,40 @@ if (exists) {
209
209
  }
210
210
  }
211
211
  ```
212
- ## EventBridge Support
212
+ ## SQS Support
213
213
 
214
- This code can be used to publish events to the EventBridge.
214
+ This code can be used to send messages to an SQS Queue.
215
215
 
216
216
  ### Example usage
217
217
  ```typescript
218
- import { initializeLogger, LogLevelEnum, publishMessage } from '@dmptool/utils';
218
+ import { initializeLogger, sendMessage, SendMessageResponse } from '@dmptool/utils';
219
219
 
220
220
  const region = 'us-west-2';
221
221
 
222
222
  // Initialize a logger
223
223
  const logger: Logger = initializeLogger('exampleSSM', LogLevelEnum.DEBUG);
224
224
 
225
- const busName = 'arn:aws:sns:us-east-1:123456789012:my-topic';
225
+ const queueURL = 'https://sqs.us-west-2.amazonaws.com/12345/my-example-function';
226
226
 
227
227
  // See the documentation for the AWS Lambda you are trying to invoke to determine what the
228
228
  // `detail-type` and `detail` payload should look like.
229
- const source = 'my-application';
230
- const detailType = 'my-event';
229
+ const source = 'my-application';
230
+ const detailType = 'my-message';
231
231
  const detail = { property1: 'value1', property2: 'value2' }
232
232
 
233
- const response = await publishMessage(
233
+ const sent: SendMessageResponse = await sendMessage(
234
234
  logger,
235
- busName,
235
+ queueURL,
236
236
  source,
237
237
  detailType,
238
238
  detail,
239
- region
239
+ region,
240
240
  );
241
241
 
242
- if (response.statusCode === 200) {
243
- console.log('Message published successfully', response.body);
242
+ if (sent && sent.status >= 200 && sent.status < 300) {
243
+ console.log('Message successfully sent', sent.status);
244
244
  } else {
245
- console.log('Error publishing message', response.body);
245
+ console.log('Error sending message', sent.status, sent.messageId);
246
246
  }
247
247
  ```
248
248
 
@@ -304,6 +304,7 @@ Environment variable requirements:
304
304
  ### Example usage
305
305
  ```typescript
306
306
  import { Logger } from 'pino';
307
+ import { Context, SQSEvent, SQSHandler, SQSBatchResponse } from 'aws-lambda';
307
308
  import { initializeLogger, LogLevel } from '@dmptool/utils';
308
309
 
309
310
  process.env.AWS_REGION = 'us-west-2';
@@ -316,14 +317,20 @@ const logger: Logger = initializeLogger('GenerateMaDMPRecordLambda', LogLevel[LO
316
317
  // Setup the LambdaRequestTracker for the logger
317
318
  const withRequest = lambdaRequestTracker();
318
319
 
319
- export const handler: Handler = async (event: EventBridgeEvent<string, EventBridgeDetails>, context: Context) => {
320
+ export const handler: SQSHandler = async (event: SQSEvent, context: Context): Promise<SQSBatchResponse> => {
320
321
  // Log the incoming event and context
321
322
  logger.debug({ event, context }, 'Received event');
322
323
 
323
324
  // Initialize the logger by setting up automatic request tracing.
324
325
  withRequest(event, context);
325
326
 
326
- logger.info({ log_level: LOG_LEVEL, foo: "bar" }, 'Hello World!');
327
+ // Loop through the records in the event
328
+ for (const record of event.Records) {
329
+ logger.info({
330
+ log_level: LOG_LEVEL,
331
+ record_body: record.body
332
+ }, 'Processing record');
333
+ }
327
334
  }
328
335
  ```
329
336
 
package/dist/general.d.ts CHANGED
@@ -20,6 +20,13 @@ export declare const toErrorMessage: (error: unknown) => string;
20
20
  * @returns a URL with the protocol converted to https
21
21
  */
22
22
  export declare const normaliseHttpProtocol: (input: string | null) => string | null;
23
+ /**
24
+ * Function to determine if the string is a valid date / time
25
+ *
26
+ * @param dateString the date string to validate
27
+ * @returns true if the date string is valid, false otherwise
28
+ */
29
+ export declare const isValidDate: (dateString: string) => boolean;
23
30
  /**
24
31
  *Function to return the current date.
25
32
  *
package/dist/general.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.areEqual = exports.removeNullAndUndefinedFromObject = exports.isNullOrUndefined = exports.randomHex = exports.convertMySQLDateTimeToRFC3339 = exports.currentDateAsString = exports.normaliseHttpProtocol = exports.toErrorMessage = exports.EnvironmentEnum = void 0;
3
+ exports.areEqual = exports.removeNullAndUndefinedFromObject = exports.isNullOrUndefined = exports.randomHex = exports.convertMySQLDateTimeToRFC3339 = exports.currentDateAsString = exports.isValidDate = exports.normaliseHttpProtocol = exports.toErrorMessage = exports.EnvironmentEnum = void 0;
4
4
  /**
5
5
  * Possible environments for the application.
6
6
  */
@@ -35,6 +35,18 @@ const normaliseHttpProtocol = (input) => {
35
35
  return input.trim().replace(/^http:\/\//, 'https://');
36
36
  };
37
37
  exports.normaliseHttpProtocol = normaliseHttpProtocol;
38
+ /**
39
+ * Function to determine if the string is a valid date / time
40
+ *
41
+ * @param dateString the date string to validate
42
+ * @returns true if the date string is valid, false otherwise
43
+ */
44
+ const isValidDate = (dateString) => {
45
+ const date = Date.parse(dateString);
46
+ // A valid date will return a number, an invalid one will return NaN
47
+ return !Number.isNaN(date);
48
+ };
49
+ exports.isValidDate = isValidDate;
38
50
  /**
39
51
  *Function to return the current date.
40
52
  *
package/dist/maDMP.js CHANGED
@@ -382,34 +382,43 @@ const loadNarrativeTemplateInfo = async (rdsConnectionParams, planId) => {
382
382
  section: [],
383
383
  };
384
384
  results.forEach((row) => {
385
- // Sections may have several rows in the results, so we need to check if we
386
- // already have the section defined.
387
- let curSection = narrative.section.find((s) => {
388
- return s.id === row.sectionId;
389
- });
390
- if (curSection === undefined || curSection === null) {
391
- curSection = {
392
- id: row.sectionId,
393
- title: row.sectionTitle,
394
- description: row.sectionDescription !== null ? row.sectionDescription : undefined,
395
- order: row.sectionOrder !== null ? row.sectionOrder : 0,
396
- question: [],
397
- };
398
- narrative.section.push(curSection);
399
- }
400
- const hasAnswer = row.answerJSON !== undefined && row.answerJSON !== null;
401
- // Every row in the results represents a single question/answer pair
402
- curSection.question.push({
403
- id: row.questionId,
404
- text: row.questionText,
405
- order: row.questionOrder !== null ? row.questionOrder : 0,
406
- answer: hasAnswer
407
- ? {
408
- id: row.answerId,
409
- json: row.answerJSON
385
+ if (row.sectionId !== null && row.sectionId !== undefined) {
386
+ // Sections may have several rows in the results, so we need to check if we
387
+ // already have the section defined.
388
+ let curSection = narrative.section.find((s) => {
389
+ return s.id === row.sectionId;
390
+ });
391
+ if (curSection === undefined || curSection === null) {
392
+ curSection = {
393
+ id: row.sectionId,
394
+ title: row.sectionTitle,
395
+ description: row.sectionDescription !== null ? row.sectionDescription : undefined,
396
+ order: row.sectionOrder !== null ? row.sectionOrder : 0,
397
+ question: [],
398
+ };
399
+ narrative.section.push(curSection);
400
+ }
401
+ if (row.questionId !== null && row.questionId !== undefined) {
402
+ const hasAnswer = row.answerJSON !== undefined && row.answerJSON !== null;
403
+ if (hasAnswer) {
404
+ // parse the nested answer value which is stored as a JSON string
405
+ const answerVal = hasAnswer ? JSON.parse(row.answerJSON.answer) : undefined;
406
+ row.answerJSON.answer = answerVal;
410
407
  }
411
- : undefined
412
- });
408
+ // Every row in the results represents a single question/answer pair
409
+ curSection.question.push({
410
+ id: row.questionId,
411
+ text: row.questionText,
412
+ order: row.questionOrder !== null ? row.questionOrder : 0,
413
+ answer: hasAnswer
414
+ ? {
415
+ id: row.answerId,
416
+ json: row.answerJSON
417
+ }
418
+ : undefined
419
+ });
420
+ }
421
+ }
413
422
  });
414
423
  return narrative;
415
424
  };
@@ -525,7 +534,7 @@ const buildContributors = (applicationName, env, planId, projectId, members, def
525
534
  * @returns the DMP metadata with extensions from the DMP Tool
526
535
  */
527
536
  const buildDMPToolExtensions = async (rdsConnectionParams, applicationName, domainName, plan, project, funding) => {
528
- var _a, _b, _c, _d, _e, _f;
537
+ var _a, _b, _c, _d;
529
538
  const extensions = {
530
539
  rda_schema_version: types_1.RDA_COMMON_STANDARD_VERSION,
531
540
  // Ignoring the `!` assertion here because we know we check the env variable
@@ -533,8 +542,8 @@ const buildDMPToolExtensions = async (rdsConnectionParams, applicationName, doma
533
542
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
534
543
  provenance: applicationName,
535
544
  featured: plan.featured ? 'yes' : 'no',
536
- privacy: (_b = (_a = plan.visibility) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'private',
537
- status: (_d = (_c = plan.status) === null || _c === void 0 ? void 0 : _c.toLowerCase()) !== null && _d !== void 0 ? _d : 'draft',
545
+ privacy: plan.visibility === 'PUBLIC' ? 'public' : 'private',
546
+ status: (_b = (_a = plan.status) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'draft',
538
547
  };
539
548
  // Generate the DMP Narrative
540
549
  const narrative = await loadNarrativeTemplateInfo(rdsConnectionParams, plan.id);
@@ -570,7 +579,7 @@ const buildDMPToolExtensions = async (rdsConnectionParams, applicationName, doma
570
579
  funder_id: funderId,
571
580
  project_identifier: {
572
581
  identifier: funding.funderProjectNumber,
573
- type: ((_e = funding.funderProjectNumber) === null || _e === void 0 ? void 0 : _e.startsWith('http'))
582
+ type: ((_c = funding.funderProjectNumber) === null || _c === void 0 ? void 0 : _c.startsWith('http'))
574
583
  ? maDMPTypes_1.StandardIdentifierType.URL
575
584
  : maDMPTypes_1.StandardIdentifierType.OTHER
576
585
  }
@@ -585,7 +594,7 @@ const buildDMPToolExtensions = async (rdsConnectionParams, applicationName, doma
585
594
  funder_id: funderId,
586
595
  opportunity_identifier: {
587
596
  identifier: funding.funderOpportunityNumber,
588
- type: ((_f = funding.funderOpportunityNumber) === null || _f === void 0 ? void 0 : _f.startsWith('http'))
597
+ type: ((_d = funding.funderOpportunityNumber) === null || _d === void 0 ? void 0 : _d.startsWith('http'))
589
598
  ? maDMPTypes_1.StandardIdentifierType.URL
590
599
  : maDMPTypes_1.StandardIdentifierType.OTHER
591
600
  }
@@ -608,8 +617,9 @@ const buildDMPToolExtensions = async (rdsConnectionParams, applicationName, doma
608
617
  extensions.funding_opportunity = [funderOpportunity];
609
618
  }
610
619
  if (!(0, general_1.isNullOrUndefined)(narrative)) {
620
+ const id = plan.dmpId.replace('https://doi.org/', '');
611
621
  extensions.narrative = {
612
- download_url: `https://${domainName}/dmps/${plan.dmpId}/narrative`,
622
+ download_url: `https://${domainName}/dmps/${id}/narrative`,
613
623
  template: narrative
614
624
  };
615
625
  }
@@ -663,8 +673,8 @@ const buildProject = (applicationName, planId, project, funding) => {
663
673
  return {
664
674
  title: project.title,
665
675
  description: (_c = project.abstractText) !== null && _c !== void 0 ? _c : undefined,
666
- start: (_d = project.startDate) !== null && _d !== void 0 ? _d : undefined,
667
- end: (_e = project.endDate) !== null && _e !== void 0 ? _e : undefined,
676
+ start: (0, general_1.isValidDate)((_d = project.startDate) !== null && _d !== void 0 ? _d : '') ? project.startDate : undefined,
677
+ end: (0, general_1.isValidDate)((_e = project.endDate) !== null && _e !== void 0 ? _e : '') ? project.endDate : undefined,
668
678
  project_id: [{
669
679
  identifier: internalIdBase(applicationName, project.id, planId),
670
680
  type: 'other'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmptool/utils",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Helper/Utility functions for use in the DMP Tool services. Particularly AWS tooling and maDMP serialization",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",