@dmptool/utils 1.0.5 → 1.0.7
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 +96 -76
- package/dist/cloudFormation.d.ts +4 -1
- package/dist/cloudFormation.js +17 -6
- package/dist/dynamo.d.ts +34 -12
- package/dist/dynamo.js +178 -86
- package/dist/eventBridge.d.ts +3 -1
- package/dist/eventBridge.js +41 -27
- package/dist/maDMP.d.ts +12 -3
- package/dist/maDMP.js +113 -73
- package/dist/rds.d.ts +11 -1
- package/dist/rds.js +33 -21
- package/dist/s3.d.ts +13 -4
- package/dist/s3.js +79 -39
- package/package.json +2 -1
package/dist/maDMP.js
CHANGED
|
@@ -9,9 +9,6 @@ const types_1 = require("@dmptool/types");
|
|
|
9
9
|
const maDMPTypes_1 = require("./maDMPTypes");
|
|
10
10
|
const ROR_REGEX = /^https?:\/\/ror\.org\/[0-9a-zA-Z]+$/;
|
|
11
11
|
const DOI_REGEX = /^(https?:\/\/)?(doi\.org\/)?(doi:)?(10\.\d{4,9}\/[-._;()/:\w]+)$/;
|
|
12
|
-
const ORCID_BASE_URL = process.env.ENV && ['stg', 'prd'].includes(process.env.ENV)
|
|
13
|
-
? 'https://orcid.org/'
|
|
14
|
-
: 'https://sandbox.orcid.org/';
|
|
15
12
|
const ORCID_REGEX = /^(https?:\/\/)?(www\.|pub\.)?(sandbox\.)?(orcid\.org\/)?([0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X])$/;
|
|
16
13
|
class DMPValidationError extends Error {
|
|
17
14
|
constructor(message) {
|
|
@@ -25,14 +22,17 @@ class DMPValidationError extends Error {
|
|
|
25
22
|
* @param orcidIn the ORCID to check
|
|
26
23
|
* @returns the ORCID in the correct format or null if it is not in the correct format
|
|
27
24
|
*/
|
|
28
|
-
function formatORCID(orcidIn) {
|
|
25
|
+
function formatORCID(env, orcidIn) {
|
|
29
26
|
// If it is blank or already in the correct format, return it
|
|
30
27
|
if (orcidIn && (orcidIn.match(ORCID_REGEX) && orcidIn.startsWith('http'))) {
|
|
31
28
|
return (0, general_1.normaliseHttpProtocol)(orcidIn);
|
|
32
29
|
}
|
|
30
|
+
const baseURL = env && ['stg', 'prd'].includes(env)
|
|
31
|
+
? 'https://orcid.org/'
|
|
32
|
+
: 'https://sandbox.orcid.org/';
|
|
33
33
|
// If it matches the ORCID format but didn't start with http then its just the id
|
|
34
34
|
if (orcidIn && orcidIn.match(ORCID_REGEX)) {
|
|
35
|
-
return (0, general_1.normaliseHttpProtocol)(`${
|
|
35
|
+
return (0, general_1.normaliseHttpProtocol)(`${baseURL}${orcidIn.split('/').pop()}`);
|
|
36
36
|
}
|
|
37
37
|
// Otherwise it's not an ORCID
|
|
38
38
|
return null;
|
|
@@ -97,21 +97,24 @@ function convertFiveCharToThreeChar(language) {
|
|
|
97
97
|
/**
|
|
98
98
|
* Function to generate the base of an internal ID namespace
|
|
99
99
|
*
|
|
100
|
+
* @param applicationName the name of the application/service
|
|
100
101
|
* @param projectId the Project ID to use for the internal ID namespace
|
|
101
102
|
* @param planId the Plan ID to use for the internal ID namespace
|
|
102
103
|
* @returns the base of the internal ID namespace
|
|
103
104
|
*/
|
|
104
|
-
function internalIdBase(projectId, planId) {
|
|
105
|
-
return `${
|
|
105
|
+
function internalIdBase(applicationName, projectId, planId) {
|
|
106
|
+
return `${applicationName}.projects.${projectId}.dmp.${planId}`;
|
|
106
107
|
}
|
|
107
108
|
/**
|
|
108
109
|
* Fetch the default MemberRole from the MySQL database
|
|
109
110
|
*
|
|
111
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
110
112
|
* @returns the default MemberRole as a string (or undefined if there is no default)
|
|
111
113
|
*/
|
|
112
|
-
const loadDefaultMemberRole = async () => {
|
|
114
|
+
const loadDefaultMemberRole = async (rdsConnectionParams) => {
|
|
113
115
|
const sql = 'SELECT * FROM memberRoles WHERE isDefault = 1';
|
|
114
|
-
|
|
116
|
+
rdsConnectionParams.logger.debug({ sql }, 'Fetching default role');
|
|
117
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, []);
|
|
115
118
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
116
119
|
return resp.results[0].id;
|
|
117
120
|
}
|
|
@@ -121,20 +124,22 @@ const loadDefaultMemberRole = async () => {
|
|
|
121
124
|
* Fetches the Plan information needed to construct the DMP Common Standard from
|
|
122
125
|
* the MySQL database
|
|
123
126
|
*
|
|
127
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
124
128
|
* @param planId the Plan ID to fetch the Plan information for
|
|
125
129
|
* @returns the Plan information needed to construct the DMP Common Standard
|
|
126
130
|
*/
|
|
127
131
|
// Fetch the Plan info needed from the MySQL database
|
|
128
|
-
const loadPlanInfo = async (planId) => {
|
|
132
|
+
const loadPlanInfo = async (rdsConnectionParams, planId) => {
|
|
129
133
|
const sql = `
|
|
130
134
|
SELECT id, dmpId, projectId, versionedTemplateId,
|
|
131
135
|
createdById, created, modifiedById, modified, title,
|
|
132
|
-
status, visibility, featured,
|
|
136
|
+
status, visibility, featured, registeredById, registered,
|
|
133
137
|
languageId
|
|
134
138
|
FROM plans
|
|
135
139
|
WHERE id = ?
|
|
136
140
|
`;
|
|
137
|
-
|
|
141
|
+
rdsConnectionParams.logger.debug({ planId, sql }, 'Fetching plan information');
|
|
142
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [planId.toString()]);
|
|
138
143
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
139
144
|
return resp.results[0];
|
|
140
145
|
}
|
|
@@ -144,16 +149,18 @@ const loadPlanInfo = async (planId) => {
|
|
|
144
149
|
* Fetches the Project information needed to construct the DMP Common Standard
|
|
145
150
|
* from the MySQL database
|
|
146
151
|
*
|
|
152
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
147
153
|
* @param projectId the Project ID to fetch the Project information for
|
|
148
154
|
* @returns the Project information needed to construct the DMP Common Standard
|
|
149
155
|
*/
|
|
150
|
-
const loadProjectInfo = async (projectId) => {
|
|
156
|
+
const loadProjectInfo = async (rdsConnectionParams, projectId) => {
|
|
151
157
|
const sql = `
|
|
152
158
|
SELECT id, title, abstractText, startDate, endDate
|
|
153
159
|
FROM projects
|
|
154
160
|
WHERE id = ?
|
|
155
161
|
`;
|
|
156
|
-
|
|
162
|
+
rdsConnectionParams.logger.debug({ projectId, sql }, 'Fetching project information');
|
|
163
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [projectId.toString()]);
|
|
157
164
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
158
165
|
return resp.results[0];
|
|
159
166
|
}
|
|
@@ -163,10 +170,11 @@ const loadProjectInfo = async (projectId) => {
|
|
|
163
170
|
* Fetches the PlanFunding information needed to construct the DMP Common Standard
|
|
164
171
|
* from the MySQL database
|
|
165
172
|
*
|
|
173
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
166
174
|
* @param planId the Plan ID to fetch the PlanFunding information for
|
|
167
175
|
* @returns the Funding information needed to construct the DMP Common Standard
|
|
168
176
|
*/
|
|
169
|
-
const loadFundingInfo = async (planId) => {
|
|
177
|
+
const loadFundingInfo = async (rdsConnectionParams, planId) => {
|
|
170
178
|
const sql = `
|
|
171
179
|
SELECT pf.id, a.uri, a.name, prf.status, prf.grantId,
|
|
172
180
|
prf.funderProjectNumber, prf.funderOpportunityNumber
|
|
@@ -175,7 +183,8 @@ const loadFundingInfo = async (planId) => {
|
|
|
175
183
|
LEFT JOIN affiliations a ON prf.affiliationId = a.uri
|
|
176
184
|
WHERE pf.planId = ?
|
|
177
185
|
`;
|
|
178
|
-
|
|
186
|
+
rdsConnectionParams.logger.debug({ planId, sql }, 'Fetching plan funding information');
|
|
187
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [planId.toString()]);
|
|
179
188
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
180
189
|
const fundings = resp.results.filter((row) => !(0, general_1.isNullOrUndefined)(row));
|
|
181
190
|
fundings.forEach((funding) => funding.status = planFundingStatusToDMPFundingStatus(funding.status));
|
|
@@ -187,10 +196,11 @@ const loadFundingInfo = async (planId) => {
|
|
|
187
196
|
* Fetches the Plan's owner information needed to construct the DMP Common Standard
|
|
188
197
|
* from the MySQL database
|
|
189
198
|
*
|
|
199
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
190
200
|
* @param ownerId the user id for the plan's owner
|
|
191
201
|
* @returns the contact information needed to construct the DMP Common Standard
|
|
192
202
|
*/
|
|
193
|
-
async function loadContactFromPlanOwner(ownerId) {
|
|
203
|
+
async function loadContactFromPlanOwner(rdsConnectionParams, ownerId) {
|
|
194
204
|
const sql = `
|
|
195
205
|
SELECT u.id, u.givenName, u.surName, u.orcid, a.uri, a.name,
|
|
196
206
|
(SELECT ue.email
|
|
@@ -200,7 +210,8 @@ async function loadContactFromPlanOwner(ownerId) {
|
|
|
200
210
|
LEFT JOIN affiliations a ON u.affiliationId = a.id
|
|
201
211
|
WHERE u.id = ?
|
|
202
212
|
`;
|
|
203
|
-
|
|
213
|
+
rdsConnectionParams.logger.debug({ ownerId, sql }, 'Fetching plan owner information');
|
|
214
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [ownerId.toString()]);
|
|
204
215
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
205
216
|
return resp.results.filter((row) => !(0, general_1.isNullOrUndefined)(row))[0];
|
|
206
217
|
}
|
|
@@ -210,10 +221,11 @@ async function loadContactFromPlanOwner(ownerId) {
|
|
|
210
221
|
* Fetches the PlanMember information needed to construct the DMP Common Standard
|
|
211
222
|
* from the MySQL database
|
|
212
223
|
*
|
|
224
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
213
225
|
* @param planId the Plan ID to fetch the PlanMember information for
|
|
214
226
|
* @returns the contributor information needed to construct the DMP Common Standard
|
|
215
227
|
*/
|
|
216
|
-
const loadMemberInfo = async (planId) => {
|
|
228
|
+
const loadMemberInfo = async (rdsConnectionParams, planId) => {
|
|
217
229
|
const sql = `
|
|
218
230
|
SELECT pc.id, a.uri, a.name, pctr.email, pctr.givenName, pctr.surName,
|
|
219
231
|
pctr.orcid, pc.isPrimaryContact, GROUP_CONCAT(r.uri) as roles
|
|
@@ -226,7 +238,8 @@ const loadMemberInfo = async (planId) => {
|
|
|
226
238
|
GROUP BY a.uri, a.name, pctr.email, pctr.givenName, pctr.surName,
|
|
227
239
|
pctr.orcid, pc.isPrimaryContact;
|
|
228
240
|
`;
|
|
229
|
-
|
|
241
|
+
rdsConnectionParams.logger.debug({ planId, sql }, 'Fetching plan member information');
|
|
242
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [planId.toString()]);
|
|
230
243
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
231
244
|
return resp.results.filter((row) => !(0, general_1.isNullOrUndefined)(row));
|
|
232
245
|
}
|
|
@@ -236,14 +249,15 @@ const loadMemberInfo = async (planId) => {
|
|
|
236
249
|
* Returns a default RDA Common Standard Dataset entry for the DMP.
|
|
237
250
|
* This is used when the Plan has no Answers to a Research Outputs question.
|
|
238
251
|
*
|
|
252
|
+
* @param applicationName the name of the application/service
|
|
239
253
|
* @param projectId the Project ID to use for the Dataset entry
|
|
240
254
|
* @param planId the Plan ID to use for the Dataset entry
|
|
241
255
|
* @returns a generic default Dataset entry
|
|
242
256
|
*/
|
|
243
|
-
const defaultDataset = (projectId, planId) => {
|
|
257
|
+
const defaultDataset = (applicationName, projectId, planId) => {
|
|
244
258
|
return {
|
|
245
259
|
dataset_id: {
|
|
246
|
-
identifier: `${internalIdBase(projectId, planId)}.outputs.1`,
|
|
260
|
+
identifier: `${internalIdBase(applicationName, projectId, planId)}.outputs.1`,
|
|
247
261
|
type: 'other'
|
|
248
262
|
},
|
|
249
263
|
personal_data: 'unknown',
|
|
@@ -257,12 +271,14 @@ const defaultDataset = (projectId, planId) => {
|
|
|
257
271
|
* from the MySQL database this information is extracted from the Answers table
|
|
258
272
|
* for Research Output Questions
|
|
259
273
|
*
|
|
274
|
+
* @param rdssConnectionParams the connection parameters for the MySQL database
|
|
275
|
+
* @param applicationName the name of the application/service
|
|
260
276
|
* @param projectId the Project ID to fetch the Dataset information for
|
|
261
277
|
* @param planId the Plan ID to fetch the Dataset information for
|
|
262
278
|
* @param language the language to use for the Dataset information
|
|
263
279
|
* @returns the dataset information needed to construct the DMP Common Standard
|
|
264
280
|
*/
|
|
265
|
-
const loadDatasetInfo = async (projectId, planId, language = 'eng') => {
|
|
281
|
+
const loadDatasetInfo = async (rdsConnectionParams, applicationName, projectId, planId, language = 'eng') => {
|
|
266
282
|
const datasets = [];
|
|
267
283
|
const sql = `
|
|
268
284
|
SELECT a.json
|
|
@@ -270,7 +286,8 @@ const loadDatasetInfo = async (projectId, planId, language = 'eng') => {
|
|
|
270
286
|
WHERE a.planId = ?
|
|
271
287
|
AND a.json LIKE '%"researchOutputsTable"%';
|
|
272
288
|
`;
|
|
273
|
-
|
|
289
|
+
rdsConnectionParams.logger.debug({ projectId, planId, sql }, 'Fetching research output information');
|
|
290
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [planId.toString()]);
|
|
274
291
|
// There would typically only be one research outputs question per plan but
|
|
275
292
|
// we need to allow for multiples just in case.
|
|
276
293
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
@@ -281,22 +298,24 @@ const loadDatasetInfo = async (projectId, planId, language = 'eng') => {
|
|
|
281
298
|
// Loop through the rows and construct the RDA Common Standard Dataset object
|
|
282
299
|
for (let idx = 0; idx < json.answer.length; idx++) {
|
|
283
300
|
const row = json.answer[idx];
|
|
284
|
-
datasets.push(buildDataset(idx, row, projectId, planId, lang));
|
|
301
|
+
datasets.push(buildDataset(applicationName, idx, row, projectId, planId, lang));
|
|
285
302
|
}
|
|
286
303
|
}
|
|
287
304
|
}
|
|
288
305
|
else {
|
|
289
|
-
|
|
306
|
+
rdsConnectionParams.logger.debug({ projectId, planId }, 'Using the default dataset');
|
|
307
|
+
return [defaultDataset(applicationName, projectId, planId)];
|
|
290
308
|
}
|
|
291
309
|
return datasets;
|
|
292
310
|
};
|
|
293
311
|
/**
|
|
294
312
|
* Builds the RDA Common Standard Related Identifier entries for the DMP
|
|
295
313
|
*
|
|
314
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
296
315
|
* @param projectId the Project ID to fetch the Related Works information for
|
|
297
316
|
* @returns the RDA Common Standard Related Identifier entries for the DMP
|
|
298
317
|
*/
|
|
299
|
-
const loadRelatedWorksInfo = async (projectId) => {
|
|
318
|
+
const loadRelatedWorksInfo = async (rdsConnectionParams, projectId) => {
|
|
300
319
|
const sql = `
|
|
301
320
|
SELECT w.doi AS identifier, LOWER(wv.workType) AS workType,
|
|
302
321
|
FROM relatedWorks rw
|
|
@@ -304,7 +323,8 @@ const loadRelatedWorksInfo = async (projectId) => {
|
|
|
304
323
|
JOIN works w ON wv.workId = w.id
|
|
305
324
|
WHERE rw.projectId = ?;
|
|
306
325
|
`;
|
|
307
|
-
|
|
326
|
+
rdsConnectionParams.logger.debug({ projectId, sql }, 'Fetching related works information');
|
|
327
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [projectId.toString()]);
|
|
308
328
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
309
329
|
const works = resp.results.filter((row) => !(0, general_1.isNullOrUndefined)(row));
|
|
310
330
|
// Determine the identifier types
|
|
@@ -323,10 +343,11 @@ const loadRelatedWorksInfo = async (projectId) => {
|
|
|
323
343
|
/**
|
|
324
344
|
* Builds the DMP Tool Narrative extension for the DMP
|
|
325
345
|
*
|
|
346
|
+
* @param rdssConnectionParams the connection parameters for the MySQL database
|
|
326
347
|
* @param planId the Plan ID to fetch the narrative information for
|
|
327
348
|
* @returns the DMP Tool Narrative extension for the DMP
|
|
328
349
|
*/
|
|
329
|
-
const loadNarrativeTemplateInfo = async (planId) => {
|
|
350
|
+
const loadNarrativeTemplateInfo = async (rdsConnectionParams, planId) => {
|
|
330
351
|
// Fetch the template, sections, questions and answers all at once
|
|
331
352
|
const sql = `
|
|
332
353
|
SELECT t.id templateId, t.name templateTitle, t.description templateDescription,
|
|
@@ -342,7 +363,8 @@ const loadNarrativeTemplateInfo = async (planId) => {
|
|
|
342
363
|
WHERE p.id = ?
|
|
343
364
|
ORDER BY s.displayOrder, q.displayOrder;
|
|
344
365
|
`;
|
|
345
|
-
|
|
366
|
+
rdsConnectionParams.logger.debug({ planId, sql }, 'Fetching narrative information');
|
|
367
|
+
const resp = await (0, rds_1.queryTable)(rdsConnectionParams, sql, [planId.toString()]);
|
|
346
368
|
let results = [];
|
|
347
369
|
// Filter out any null or undefined results
|
|
348
370
|
if (resp && Array.isArray(resp.results) && resp.results.length > 0) {
|
|
@@ -388,12 +410,13 @@ const loadNarrativeTemplateInfo = async (planId) => {
|
|
|
388
410
|
/**
|
|
389
411
|
* Builds the RDA Common Standard Contact entry for the DMP
|
|
390
412
|
*
|
|
413
|
+
* @param rdssConnectionParams the connection parameters for the MySQL database
|
|
391
414
|
* @param plan the Plan information retrieve from the MySQL database
|
|
392
415
|
* @param members the PlanMembers information retrieve from the MySQL database
|
|
393
416
|
* @returns the RDA Common Standard Contact entry for the DMP
|
|
394
417
|
* @throws DMPValidationError if no primary contact is found for the DMP
|
|
395
418
|
*/
|
|
396
|
-
const buildContact = async (plan, members) => {
|
|
419
|
+
const buildContact = async (rdsConnectionParams, env, plan, members) => {
|
|
397
420
|
// Extract the primary contact from the members
|
|
398
421
|
const memberContact = members.find((c) => {
|
|
399
422
|
return c === null || c === void 0 ? void 0 : c.isPrimaryContact;
|
|
@@ -401,9 +424,9 @@ const buildContact = async (plan, members) => {
|
|
|
401
424
|
// If no primary contact is available, use the plan owner
|
|
402
425
|
const primary = memberContact && memberContact.email
|
|
403
426
|
? memberContact
|
|
404
|
-
: await loadContactFromPlanOwner(Number(plan.createdById));
|
|
427
|
+
: await loadContactFromPlanOwner(rdsConnectionParams, Number(plan.createdById));
|
|
405
428
|
if (primary && primary.email) {
|
|
406
|
-
const orcid = primary.orcid ? formatORCID(primary.orcid) : null;
|
|
429
|
+
const orcid = primary.orcid ? formatORCID(env, primary.orcid) : null;
|
|
407
430
|
// Build the contact entry for the DMP
|
|
408
431
|
const contactEntry = {
|
|
409
432
|
contact_id: [{
|
|
@@ -436,13 +459,14 @@ const buildContact = async (plan, members) => {
|
|
|
436
459
|
/**
|
|
437
460
|
* Builds the RDA Common Standard Contributor array for the DMP from the PlanMembers
|
|
438
461
|
*
|
|
462
|
+
* @param applicationName the name of the application/service
|
|
439
463
|
* @param planId the Plan ID
|
|
440
464
|
* @param projectId the Project ID
|
|
441
465
|
* @param members the PlanMembers information retrieve from the MySQL database
|
|
442
466
|
* @param defaultRole the default role to use if the member doesn't have a role'
|
|
443
467
|
* @returns the RDA Common Standard Contributor array for the DMP
|
|
444
468
|
*/
|
|
445
|
-
const buildContributors = (planId, projectId, members, defaultRole) => {
|
|
469
|
+
const buildContributors = (applicationName, env, planId, projectId, members, defaultRole) => {
|
|
446
470
|
if (!Array.isArray(members) || members.length <= 0) {
|
|
447
471
|
return [];
|
|
448
472
|
}
|
|
@@ -458,7 +482,7 @@ const buildContributors = (planId, projectId, members, defaultRole) => {
|
|
|
458
482
|
role: roles,
|
|
459
483
|
};
|
|
460
484
|
// Use the member's ORCID if it exists, otherwise generate a new one'
|
|
461
|
-
const formatted = member.orcid ? formatORCID(member.orcid) : null;
|
|
485
|
+
const formatted = member.orcid ? formatORCID(env, member.orcid) : null;
|
|
462
486
|
if (formatted !== null) {
|
|
463
487
|
contrib.contributor_id = [{
|
|
464
488
|
identifier: formatted,
|
|
@@ -468,7 +492,7 @@ const buildContributors = (planId, projectId, members, defaultRole) => {
|
|
|
468
492
|
else {
|
|
469
493
|
// RDA Common Standard requires an id so generate one
|
|
470
494
|
contrib.contributor_id = [{
|
|
471
|
-
identifier: `${internalIdBase(projectId, planId)}.members.${member.id}`,
|
|
495
|
+
identifier: `${internalIdBase(applicationName, projectId, planId)}.members.${member.id}`,
|
|
472
496
|
type: 'other'
|
|
473
497
|
}];
|
|
474
498
|
}
|
|
@@ -488,25 +512,28 @@ const buildContributors = (planId, projectId, members, defaultRole) => {
|
|
|
488
512
|
/**
|
|
489
513
|
* Builds the DMP Tool extensions to the RDA Common Standard
|
|
490
514
|
*
|
|
515
|
+
* @param rdsConnectionParams the connection parameters for the MySQL database
|
|
516
|
+
* @param applicationName the name of the application/service
|
|
517
|
+
* @param domainName the domain name of the DMP Tool
|
|
491
518
|
* @param plan the Plan information retrieve from the MySQL database
|
|
492
519
|
* @param project the Project information retrieve from the MySQL database
|
|
493
520
|
* @param funding the Funding information retrieve from the MySQL database
|
|
494
521
|
* @returns the DMP metadata with extensions from the DMP Tool
|
|
495
522
|
*/
|
|
496
|
-
const buildDMPToolExtensions = async (plan, project, funding) => {
|
|
523
|
+
const buildDMPToolExtensions = async (rdsConnectionParams, applicationName, domainName, plan, project, funding) => {
|
|
497
524
|
var _a, _b, _c, _d, _e, _f;
|
|
498
525
|
const extensions = {
|
|
499
526
|
rda_schema_version: types_1.RDA_COMMON_STANDARD_VERSION,
|
|
500
527
|
// Ignoring the `!` assertion here because we know we check the env variable
|
|
501
528
|
// when the entrypoint function is called.
|
|
502
529
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
503
|
-
provenance:
|
|
530
|
+
provenance: applicationName,
|
|
504
531
|
featured: plan.featured ? 'yes' : 'no',
|
|
505
532
|
privacy: (_b = (_a = plan.visibility) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'private',
|
|
506
533
|
status: (_d = (_c = plan.status) === null || _c === void 0 ? void 0 : _c.toLowerCase()) !== null && _d !== void 0 ? _d : 'draft',
|
|
507
534
|
};
|
|
508
535
|
// Generate the DMP Narrative
|
|
509
|
-
const narrative = await loadNarrativeTemplateInfo(plan.id);
|
|
536
|
+
const narrative = await loadNarrativeTemplateInfo(rdsConnectionParams, plan.id);
|
|
510
537
|
// Fetch the research domain if one was specified
|
|
511
538
|
const research_domain = project.dmptool_research_domain
|
|
512
539
|
? { name: project.dmptool_research_domain }
|
|
@@ -515,12 +542,12 @@ const buildDMPToolExtensions = async (plan, project, funding) => {
|
|
|
515
542
|
let funderOpportunity = undefined;
|
|
516
543
|
if (funding) {
|
|
517
544
|
const projectId = {
|
|
518
|
-
identifier: internalIdBase(project.id, plan.id),
|
|
545
|
+
identifier: internalIdBase(applicationName, project.id, plan.id),
|
|
519
546
|
type: maDMPTypes_1.StandardIdentifierType.OTHER
|
|
520
547
|
};
|
|
521
548
|
const funderId = funding.uri === undefined
|
|
522
549
|
? {
|
|
523
|
-
identifier: `${internalIdBase(project.id, plan.id)}.fundings.${funding.id}`,
|
|
550
|
+
identifier: `${internalIdBase(applicationName, project.id, plan.id)}.fundings.${funding.id}`,
|
|
524
551
|
type: maDMPTypes_1.StandardIdentifierType.OTHER
|
|
525
552
|
}
|
|
526
553
|
: {
|
|
@@ -577,7 +604,7 @@ const buildDMPToolExtensions = async (plan, project, funding) => {
|
|
|
577
604
|
}
|
|
578
605
|
if (!(0, general_1.isNullOrUndefined)(narrative)) {
|
|
579
606
|
extensions.narrative = {
|
|
580
|
-
download_url: `https://${
|
|
607
|
+
download_url: `https://${domainName}/dmps/${plan.dmpId}/narrative`,
|
|
581
608
|
template: narrative
|
|
582
609
|
};
|
|
583
610
|
}
|
|
@@ -586,12 +613,13 @@ const buildDMPToolExtensions = async (plan, project, funding) => {
|
|
|
586
613
|
/**
|
|
587
614
|
* Builds the Project and Funding info for the RDA Common Standard
|
|
588
615
|
*
|
|
616
|
+
* @param applicationName the name of the application/service
|
|
589
617
|
* @param planId the Plan ID
|
|
590
618
|
* @param project the Project information retrieve from the MySQL database
|
|
591
619
|
* @param funding the Funding information retrieve from the MySQL database
|
|
592
620
|
* @returns the Project and Funding info for the RDA Common Standard
|
|
593
621
|
*/
|
|
594
|
-
const buildProject = (planId, project, funding) => {
|
|
622
|
+
const buildProject = (applicationName, planId, project, funding) => {
|
|
595
623
|
var _a, _b, _c, _d, _e;
|
|
596
624
|
if ((0, general_1.isNullOrUndefined)(project)) {
|
|
597
625
|
return undefined;
|
|
@@ -611,7 +639,7 @@ const buildProject = (planId, project, funding) => {
|
|
|
611
639
|
type: ((_b = (funding).uri) === null || _b === void 0 ? void 0 : _b.match(ROR_REGEX)) ? 'ror' : 'url'
|
|
612
640
|
}
|
|
613
641
|
: {
|
|
614
|
-
identifier: `${internalIdBase(project.id, planId)}.fundings.${funding.id}`,
|
|
642
|
+
identifier: `${internalIdBase(applicationName, project.id, planId)}.fundings.${funding.id}`,
|
|
615
643
|
type: 'other'
|
|
616
644
|
};
|
|
617
645
|
// The RDA Common Standard requires the funder name and status to be present
|
|
@@ -633,7 +661,7 @@ const buildProject = (planId, project, funding) => {
|
|
|
633
661
|
start: (_d = project.startDate) !== null && _d !== void 0 ? _d : undefined,
|
|
634
662
|
end: (_e = project.endDate) !== null && _e !== void 0 ? _e : undefined,
|
|
635
663
|
project_id: [{
|
|
636
|
-
identifier: internalIdBase(project.id, planId),
|
|
664
|
+
identifier: internalIdBase(applicationName, project.id, planId),
|
|
637
665
|
type: 'other'
|
|
638
666
|
}],
|
|
639
667
|
funding: fundingObject
|
|
@@ -677,6 +705,7 @@ const byteSizeToBytes = (size) => {
|
|
|
677
705
|
* Convert a @dmptool/types researchOutputTable answer row into an RDA Common
|
|
678
706
|
* Standard Dataset object.
|
|
679
707
|
*
|
|
708
|
+
* @param applicationName the name of the application/service
|
|
680
709
|
* @param rowIdx the index of the answer row
|
|
681
710
|
* @param row the answer row
|
|
682
711
|
* @param projectId the ID of the project that the dataset belongs to
|
|
@@ -684,7 +713,7 @@ const byteSizeToBytes = (size) => {
|
|
|
684
713
|
* @param language the language of the dataset (defaults to 'eng')
|
|
685
714
|
* @returns a RDA Common Standard Dataset object
|
|
686
715
|
*/
|
|
687
|
-
const buildDataset = (rowIdx, row, projectId, planId, language = 'eng') => {
|
|
716
|
+
const buildDataset = (applicationName, rowIdx, row, projectId, planId, language = 'eng') => {
|
|
688
717
|
const title = findColumnById('title', row.columns);
|
|
689
718
|
const desc = findColumnById('description', row.columns);
|
|
690
719
|
const typ = findColumnById('type', row.columns);
|
|
@@ -761,7 +790,7 @@ const buildDataset = (rowIdx, row, projectId, planId, language = 'eng') => {
|
|
|
761
790
|
type: (0, general_1.isNullOrUndefined)(typ) ? 'dataset' : typ.answer,
|
|
762
791
|
description: (0, general_1.isNullOrUndefined)(desc) ? undefined : desc.answer,
|
|
763
792
|
dataset_id: {
|
|
764
|
-
identifier: `${internalIdBase(projectId, planId)}.outputs.${rowIdx + 1}`,
|
|
793
|
+
identifier: `${internalIdBase(applicationName, projectId, planId)}.outputs.${rowIdx + 1}`,
|
|
765
794
|
type: 'other'
|
|
766
795
|
},
|
|
767
796
|
personal_data: (0, general_1.isNullOrUndefined)(flags) ?
|
|
@@ -800,11 +829,13 @@ const buildDataset = (rowIdx, row, projectId, planId, language = 'eng') => {
|
|
|
800
829
|
* Validate the specified DMP metadata record against the RDA Common Standard
|
|
801
830
|
* and DMP Tool extensions schema
|
|
802
831
|
*
|
|
832
|
+
* @param logger the logger to use for logging
|
|
803
833
|
* @param dmp The DMP metadata record to validate
|
|
804
834
|
* @returns the DMP metadata record if it is valid
|
|
805
835
|
* @throws DMPValidationError if the record is invalid with the error message(s)
|
|
806
836
|
*/
|
|
807
|
-
const validateRDACommonStandard = (dmp) => {
|
|
837
|
+
const validateRDACommonStandard = (logger, dmp) => {
|
|
838
|
+
var _a, _b;
|
|
808
839
|
const validationErrors = [];
|
|
809
840
|
const validator = new jsonschema_1.Validator();
|
|
810
841
|
// Validate against the RDA Common Standard schema
|
|
@@ -813,7 +844,9 @@ const validateRDACommonStandard = (dmp) => {
|
|
|
813
844
|
validationErrors.push(...rdaResult.errors.map(e => `${e.path.join('.')} - ${e.message}`));
|
|
814
845
|
}
|
|
815
846
|
if (validationErrors.length > 0) {
|
|
816
|
-
|
|
847
|
+
const msg = `Invalid RDA Common Standard: ${validationErrors.join('; ')}`;
|
|
848
|
+
logger.warn({ dmpId: (_b = (_a = dmp === null || dmp === void 0 ? void 0 : dmp.dmp) === null || _a === void 0 ? void 0 : _a.dmp_id) === null || _b === void 0 ? void 0 : _b.identifier }, msg);
|
|
849
|
+
throw new DMPValidationError(msg);
|
|
817
850
|
}
|
|
818
851
|
return dmp;
|
|
819
852
|
};
|
|
@@ -822,11 +855,12 @@ exports.validateRDACommonStandard = validateRDACommonStandard;
|
|
|
822
855
|
* Validate the specified DMP metadata record against the RDA Common Standard
|
|
823
856
|
* and DMP Tool extensions schema
|
|
824
857
|
*
|
|
858
|
+
* @param logger the logger to use for logging
|
|
825
859
|
* @param dmp The DMP metadata record to validate
|
|
826
860
|
* @returns the DMP metadata record if it is valid
|
|
827
861
|
* @throws DMPValidationError if the record is invalid with the error message(s)
|
|
828
862
|
*/
|
|
829
|
-
const validateDMPToolExtensions = (dmp) => {
|
|
863
|
+
const validateDMPToolExtensions = (logger, dmpId, dmp) => {
|
|
830
864
|
var _a;
|
|
831
865
|
const validationErrors = [];
|
|
832
866
|
// Next validate against the DMP Tool extension schema
|
|
@@ -835,7 +869,9 @@ const validateDMPToolExtensions = (dmp) => {
|
|
|
835
869
|
validationErrors.push(...extResult.error.issues.map(e => `${e.path.join('.')} - ${e.message}`));
|
|
836
870
|
}
|
|
837
871
|
if (validationErrors.length > 0) {
|
|
838
|
-
|
|
872
|
+
const msg = `Invalid DMP Tool extensions: ${validationErrors.join('; ')}`;
|
|
873
|
+
logger.warn({ dmpId }, msg);
|
|
874
|
+
throw new DMPValidationError(msg);
|
|
839
875
|
}
|
|
840
876
|
return dmp;
|
|
841
877
|
};
|
|
@@ -877,48 +913,51 @@ const cleanRDACommonStandard = (plan, dmp) => {
|
|
|
877
913
|
* - The `registered` indicates whether the DMP is published/registered with DataCite/EZID
|
|
878
914
|
* - The `tombstoned` indicates that it was published/registered but is now removed
|
|
879
915
|
*
|
|
916
|
+
* @param rdsConnectionParams the connection parameters for the RDS instance
|
|
917
|
+
* @param applicationName the name of the application/service
|
|
918
|
+
* @param domainName the domain name of the application/service website
|
|
880
919
|
* @param planId the ID of the plan to generate the DMP for
|
|
920
|
+
* @param env The environment from EnvironmentEnum (defaults to EnvironmentEnum.DEV)
|
|
881
921
|
* @returns a JSON representation of the DMP
|
|
882
922
|
*/
|
|
883
|
-
async function planToDMPCommonStandard(planId) {
|
|
884
|
-
if (
|
|
885
|
-
throw new Error('
|
|
886
|
-
}
|
|
887
|
-
if ((0, general_1.isNullOrUndefined)(process.env.DOMAIN_NAME)) {
|
|
888
|
-
throw new Error('DOMAIN_NAME environment variable is not set');
|
|
889
|
-
}
|
|
890
|
-
if ((0, general_1.isNullOrUndefined)(process.env.APPLICATION_NAME)) {
|
|
891
|
-
throw new Error('APPLICATION_NAME environment variable is not set');
|
|
923
|
+
async function planToDMPCommonStandard(rdsConnectionParams, applicationName, domainName, env = general_1.EnvironmentEnum.DEV, planId) {
|
|
924
|
+
if (!rdsConnectionParams || !applicationName || !domainName || !planId) {
|
|
925
|
+
throw new Error('Invalid arguments provided to planToDMPCommonStandard');
|
|
892
926
|
}
|
|
893
927
|
// Fetch the Plan data
|
|
894
|
-
const plan = await loadPlanInfo(planId);
|
|
928
|
+
const plan = await loadPlanInfo(rdsConnectionParams, planId);
|
|
895
929
|
if (plan === undefined) {
|
|
930
|
+
rdsConnectionParams.logger.error({ planId, applicationName, env }, 'Plan not found');
|
|
896
931
|
throw new DMPValidationError(`Plan not found: ${planId}`);
|
|
897
932
|
}
|
|
898
933
|
if ((0, general_1.isNullOrUndefined)(plan.title)) {
|
|
934
|
+
rdsConnectionParams.logger.error({ planId, applicationName, env }, 'Plan title not found');
|
|
899
935
|
throw new DMPValidationError(`Plan title not found for plan: ${planId}`);
|
|
900
936
|
}
|
|
901
937
|
if ((0, general_1.isNullOrUndefined)(plan.dmpId)) {
|
|
938
|
+
rdsConnectionParams.logger.error({ planId, applicationName, env }, 'Plan dmpId not found');
|
|
902
939
|
throw new DMPValidationError(`DMP ID not found for plan: ${planId}`);
|
|
903
940
|
}
|
|
904
941
|
// Get the Project data
|
|
905
|
-
const project = await loadProjectInfo(plan.projectId);
|
|
942
|
+
const project = await loadProjectInfo(rdsConnectionParams, plan.projectId);
|
|
906
943
|
if (project === undefined || !project.title) {
|
|
944
|
+
rdsConnectionParams.logger.error({ planId, applicationName, env }, 'Project not found');
|
|
907
945
|
throw new DMPValidationError(`Project not found: ${plan.projectId}`);
|
|
908
946
|
}
|
|
909
947
|
// Get all the plan members and determine the primary contact
|
|
910
|
-
const members = plan.id ? await loadMemberInfo(plan.id) : [];
|
|
911
|
-
const contact = await buildContact(plan, members);
|
|
948
|
+
const members = plan.id ? await loadMemberInfo(rdsConnectionParams, plan.id) : [];
|
|
949
|
+
const contact = await buildContact(rdsConnectionParams, env, plan, members);
|
|
912
950
|
if (!contact) {
|
|
951
|
+
rdsConnectionParams.logger.error({ planId, applicationName, env }, 'Could not build primary contact');
|
|
913
952
|
throw new DMPValidationError(`Could not establish a primary contact for plan: ${planId}`);
|
|
914
953
|
}
|
|
915
954
|
// Get all the funding and narrative info
|
|
916
|
-
const datasets = await loadDatasetInfo(project.id, plan.id, plan.languageId);
|
|
955
|
+
const datasets = await loadDatasetInfo(rdsConnectionParams, applicationName, project.id, plan.id, plan.languageId);
|
|
917
956
|
// We only allow one funding per plan at this time
|
|
918
|
-
const fundings = await loadFundingInfo(plan.id);
|
|
957
|
+
const fundings = await loadFundingInfo(rdsConnectionParams, plan.id);
|
|
919
958
|
const funding = fundings.length > 0 ? fundings[0] : undefined;
|
|
920
|
-
const works = await loadRelatedWorksInfo(plan.projectId);
|
|
921
|
-
const defaultRole = await loadDefaultMemberRole();
|
|
959
|
+
const works = await loadRelatedWorksInfo(rdsConnectionParams, plan.projectId);
|
|
960
|
+
const defaultRole = await loadDefaultMemberRole(rdsConnectionParams);
|
|
922
961
|
// If the plan is registered, use the DOI as the identifier, otherwise convert to a URL
|
|
923
962
|
const dmpId = plan.registered
|
|
924
963
|
? {
|
|
@@ -926,7 +965,7 @@ async function planToDMPCommonStandard(planId) {
|
|
|
926
965
|
type: 'doi'
|
|
927
966
|
}
|
|
928
967
|
: {
|
|
929
|
-
identifier: `https://${
|
|
968
|
+
identifier: `https://${domainName}/projects/${project.id}/dmp/${plan.id}`,
|
|
930
969
|
type: 'url'
|
|
931
970
|
};
|
|
932
971
|
// Examine the datasets to determine the status of ethical issues
|
|
@@ -958,8 +997,8 @@ async function planToDMPCommonStandard(planId) {
|
|
|
958
997
|
dataset: datasets,
|
|
959
998
|
},
|
|
960
999
|
};
|
|
961
|
-
const dmpProject = buildProject(plan.id, project, funding);
|
|
962
|
-
const dmpContributor = buildContributors(plan.id, project.id, members, defaultRole !== null && defaultRole !== void 0 ? defaultRole : 'other');
|
|
1000
|
+
const dmpProject = buildProject(applicationName, plan.id, project, funding);
|
|
1001
|
+
const dmpContributor = buildContributors(applicationName, env, plan.id, project.id, members, defaultRole !== null && defaultRole !== void 0 ? defaultRole : 'other');
|
|
963
1002
|
// Add the contributor, project and related identifier properties if they have values
|
|
964
1003
|
if (!(0, general_1.isNullOrUndefined)(dmpProject)) {
|
|
965
1004
|
dmp.dmp.project = [(0, general_1.removeNullAndUndefinedFromObject)(dmpProject)];
|
|
@@ -974,9 +1013,10 @@ async function planToDMPCommonStandard(planId) {
|
|
|
974
1013
|
}
|
|
975
1014
|
const cleaned = cleanRDACommonStandard(plan, dmp);
|
|
976
1015
|
// Generate the DMP Tool extensions to the RDA Common Standard
|
|
977
|
-
const extensions = await buildDMPToolExtensions(plan, project, funding);
|
|
1016
|
+
const extensions = await buildDMPToolExtensions(rdsConnectionParams, applicationName, domainName, plan, project, funding);
|
|
1017
|
+
rdsConnectionParams.logger.debug({ applicationName, domainName, planId, env, dmpId: plan.dmpId }, 'Generated maDMP metadata record');
|
|
978
1018
|
// Return the combined DMP metadata record
|
|
979
1019
|
return {
|
|
980
|
-
dmp: Object.assign(Object.assign({}, (0, exports.validateRDACommonStandard)(cleaned).dmp), (0, exports.validateDMPToolExtensions)(extensions))
|
|
1020
|
+
dmp: Object.assign(Object.assign({}, (0, exports.validateRDACommonStandard)(rdsConnectionParams.logger, cleaned).dmp), (0, exports.validateDMPToolExtensions)(rdsConnectionParams.logger, plan.dmpId, extensions))
|
|
981
1021
|
};
|
|
982
1022
|
}
|
package/dist/rds.d.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
export interface ConnectionParams {
|
|
3
|
+
logger: Logger;
|
|
4
|
+
host: string;
|
|
5
|
+
port: number;
|
|
6
|
+
user: string;
|
|
7
|
+
password: string;
|
|
8
|
+
database: string;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* Function to run a SQL query against the MySQL database.
|
|
3
12
|
*
|
|
4
13
|
* @param query the SQL query to run
|
|
14
|
+
* @param connectionParams the connection parameters to use for the connection
|
|
5
15
|
* @param params the parameters to use in the query
|
|
6
16
|
* @returns the results of the query
|
|
7
17
|
*/
|
|
8
|
-
export declare const queryTable: (query: string, params?: any[]) => Promise<{
|
|
18
|
+
export declare const queryTable: (connectionParams: ConnectionParams, query: string, params?: any[]) => Promise<{
|
|
9
19
|
results: any[];
|
|
10
20
|
fields: any[];
|
|
11
21
|
}>;
|