@researchdatabox/redbox-dev-tools 0.0.1-beta.8936 → 0.0.1-beta.9018

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.
@@ -1,6 +1,10 @@
1
1
  // This file is a helper for dev only. It makes it easier to run webpack when parts of the mono repo are in different stages of being built. Refer to the redbox-core webpack.ts for how the hook runs webpack
2
+ const path = require('path');
3
+ const { createRequire } = require('module');
2
4
  const redboxCore = require('@researchdatabox/redbox-core');
3
5
 
6
+ const requireFromHere = createRequire(__filename);
7
+
4
8
  const ensureTsNode = () => {
5
9
  try {
6
10
  require('ts-node/register/transpile-only');
@@ -18,7 +22,7 @@ if (!defineWebpackHook) {
18
22
  }
19
23
  if (!defineWebpackHook && ensureTsNode()) {
20
24
  try {
21
- defineWebpackHook = require('@researchdatabox/redbox-core/src/hooks/webpack.ts').defineWebpackHook;
25
+ defineWebpackHook = requireFromHere('@researchdatabox/redbox-core/src/hooks/webpack.ts').defineWebpackHook;
22
26
  } catch (e) {}
23
27
  }
24
28
 
@@ -54,7 +58,7 @@ if (!webpackConfig || (Array.isArray(webpackConfig) && webpackConfig.length ===
54
58
  if (!webpackConfig || (Array.isArray(webpackConfig) && webpackConfig.length === 0)) {
55
59
  if (ensureTsNode()) {
56
60
  try {
57
- const srcWebpack = require('@researchdatabox/redbox-core/src/config/webpack.config.ts');
61
+ const srcWebpack = requireFromHere('@researchdatabox/redbox-core/src/config/webpack.config.ts');
58
62
  webpackConfig = srcWebpack && srcWebpack.webpack ? srcWebpack.webpack.config : srcWebpack.config || srcWebpack;
59
63
  } catch (e) {}
60
64
  }
package/dist/cli.js CHANGED
@@ -128,6 +128,7 @@ program
128
128
  });
129
129
  (0, form_config_1.registerMigrateFormConfigCommand)(program);
130
130
  (0, form_config_1.registerMigrateDataClassificationCommand)(program);
131
+ (0, form_config_1.registerMigrateFigshareConfigCommand)(program);
131
132
  (0, form_config_1.registerClientFormConfigCommand)(program);
132
133
  (0, form_config_1.registerQuestionTreeDiagramCommand)(program);
133
134
  program
@@ -3,3 +3,4 @@ export declare function registerMigrateFormConfigCommand(program: Command): void
3
3
  export declare function registerMigrateDataClassificationCommand(program: Command): void;
4
4
  export declare function registerClientFormConfigCommand(program: Command): void;
5
5
  export declare function registerQuestionTreeDiagramCommand(program: Command): void;
6
+ export declare function registerMigrateFigshareConfigCommand(program: Command): void;
@@ -40,6 +40,7 @@ exports.registerMigrateFormConfigCommand = registerMigrateFormConfigCommand;
40
40
  exports.registerMigrateDataClassificationCommand = registerMigrateDataClassificationCommand;
41
41
  exports.registerClientFormConfigCommand = registerClientFormConfigCommand;
42
42
  exports.registerQuestionTreeDiagramCommand = registerQuestionTreeDiagramCommand;
43
+ exports.registerMigrateFigshareConfigCommand = registerMigrateFigshareConfigCommand;
43
44
  const fs_1 = __importDefault(require("fs"));
44
45
  const path = __importStar(require("path"));
45
46
  const redbox_core_1 = require("@researchdatabox/redbox-core");
@@ -192,3 +193,212 @@ function registerQuestionTreeDiagramCommand(program) {
192
193
  }
193
194
  });
194
195
  }
196
+ function collectLegacyTemplates(value, pathPrefix = '') {
197
+ if (typeof value === 'string') {
198
+ return value.includes('<%') ? [pathPrefix] : [];
199
+ }
200
+ if (Array.isArray(value)) {
201
+ return value.flatMap((entry, index) => collectLegacyTemplates(entry, `${pathPrefix}[${index}]`));
202
+ }
203
+ if (value && typeof value === 'object') {
204
+ return Object.entries(value).flatMap(([key, entry]) => collectLegacyTemplates(entry, pathPrefix ? `${pathPrefix}.${key}` : key));
205
+ }
206
+ return [];
207
+ }
208
+ function loadLegacyModule(modulePath) {
209
+ const resolvedPath = path.resolve(modulePath);
210
+ const loaded = require(resolvedPath);
211
+ if (loaded == null || typeof loaded !== 'object') {
212
+ throw new Error(`Legacy config module '${resolvedPath}' did not export an object`);
213
+ }
214
+ return loaded;
215
+ }
216
+ function pickLegacyExport(moduleExports, exportName) {
217
+ const named = moduleExports[exportName];
218
+ if (named != null && typeof named === 'object') {
219
+ return named;
220
+ }
221
+ return moduleExports;
222
+ }
223
+ function toNumericStatusCodes(value) {
224
+ if (!Array.isArray(value)) {
225
+ return [408, 429, 500, 502, 503, 504];
226
+ }
227
+ return value
228
+ .map((entry) => Number(entry))
229
+ .filter((entry) => Number.isFinite(entry));
230
+ }
231
+ function createMigratedFigsharePublishingConfig(brand, figshareApi, figshareApiEnv = {}, figshareForMapping = {}) {
232
+ const overrideArtifacts = figshareApiEnv.overrideArtifacts ?? {};
233
+ const mapping = {
234
+ ...(figshareApi.mapping ?? {}),
235
+ ...(overrideArtifacts.mapping ?? {})
236
+ };
237
+ const resolvedBaseUrl = String(overrideArtifacts.baseURL ?? figshareApi.baseURL ?? '');
238
+ const resolvedFrontEndUrl = String(overrideArtifacts.frontEndURL ?? figshareApi.frontEndURL ?? '');
239
+ const resolvedToken = String(overrideArtifacts.APIToken ?? figshareApi.APIToken ?? '');
240
+ return {
241
+ enabled: Boolean(resolvedToken && resolvedBaseUrl && resolvedFrontEndUrl),
242
+ connection: {
243
+ baseUrl: resolvedBaseUrl,
244
+ frontEndUrl: resolvedFrontEndUrl,
245
+ token: resolvedToken,
246
+ timeoutMs: 30000,
247
+ operationTimeouts: {
248
+ metadataMs: 30000,
249
+ uploadInitMs: 30000,
250
+ uploadPartMs: 120000,
251
+ publishMs: 60000
252
+ },
253
+ retry: {
254
+ maxAttempts: Number(figshareApi.retry?.maxAttempts ?? 3),
255
+ baseDelayMs: Number(figshareApi.retry?.baseDelayMs ?? 500),
256
+ maxDelayMs: Number(figshareApi.retry?.maxDelayMs ?? 4000),
257
+ retryOnStatusCodes: toNumericStatusCodes(figshareApi.retry?.retryOnStatusCodes),
258
+ retryOnMethods: ['get', 'put', 'delete']
259
+ }
260
+ },
261
+ article: {
262
+ itemType: mapping.figshareItemType ?? 'dataset',
263
+ groupId: mapping.figshareItemGroupId,
264
+ publishMode: mapping.figshareNeedsPublishAfterFileUpload ? 'afterUploadsComplete' : 'immediate',
265
+ republishOnMetadataChange: true,
266
+ republishOnAssetChange: true
267
+ },
268
+ record: {
269
+ articleIdPath: mapping.recordFigArticleId ?? 'metadata.figshare_article_id',
270
+ articleUrlPaths: Array.isArray(mapping.recordFigArticleURL) ? mapping.recordFigArticleURL : [mapping.recordFigArticleURL ?? 'metadata.figshare_article_location'],
271
+ dataLocationsPath: mapping.recordDataLocations ?? 'metadata.dataLocations',
272
+ statusPath: 'metadata.figshareStatus',
273
+ errorPath: 'metadata.figshareError',
274
+ syncStatePath: 'metadata.figshareSyncState',
275
+ allFilesUploadedPath: typeof mapping.recordAllFilesUploaded === 'string' ? mapping.recordAllFilesUploaded : ''
276
+ },
277
+ selection: {
278
+ attachmentMode: mapping.figshareOnlyPublishSelectedAttachmentFiles ? 'selectedOnly' : 'all',
279
+ urlMode: mapping.figshareOnlyPublishSelectedLocationURLs ? 'selectedOnly' : 'all',
280
+ selectedFlagPath: 'selected'
281
+ },
282
+ authors: {
283
+ source: 'defaultRedboxContributors',
284
+ uniqueBy: mapping.recordAuthorUniqueBy ?? 'email',
285
+ externalNameField: mapping.recordAuthorExternalName ?? 'text_full_name',
286
+ maxInlineAuthors: 50,
287
+ contributorPaths: ['metadata.contributor_ci', 'metadata.contributors'],
288
+ lookup: []
289
+ },
290
+ metadata: {
291
+ title: { kind: 'path', path: 'metadata.title' },
292
+ description: { kind: 'path', path: 'metadata.description' },
293
+ keywords: { kind: 'path', path: 'metadata.finalKeywords', defaultValue: [] },
294
+ funding: { kind: 'path', path: 'metadata.funder' },
295
+ license: { source: { kind: 'path', path: mapping.recordLicensePath ?? 'metadata.license' }, matchBy: 'urlContains', required: true },
296
+ categories: { source: { kind: 'path', path: mapping.recordCategoryPath ?? 'metadata.forCodes', defaultValue: [] }, mappingStrategy: 'for2020Mapping' },
297
+ customFields: []
298
+ },
299
+ categories: {
300
+ strategy: 'for2020Mapping',
301
+ mappingTable: ((figshareForMapping.FORMapping ?? []).map((entry) => ({
302
+ sourceCode: String(entry.redboxCode ?? entry.sourceCode ?? ''),
303
+ figshareCategoryId: Number(entry.figshareCategoryId ?? entry.figCode ?? 0)
304
+ }))).filter((entry) => entry.sourceCode !== ''),
305
+ allowUnmapped: false
306
+ },
307
+ assets: {
308
+ enableHostedFiles: true,
309
+ enableLinkFiles: true,
310
+ dedupeStrategy: 'sourceId',
311
+ staging: {
312
+ tempDir: String(figshareApi.attachmentsFigshareTempDir ?? ''),
313
+ cleanupPolicy: 'deleteAfterSuccess',
314
+ diskSpaceThresholdBytes: 1073741824
315
+ }
316
+ },
317
+ embargo: {
318
+ mode: 'recordDriven',
319
+ forceSync: Boolean(mapping.figshareForceEmbargoUpdateAlways),
320
+ accessRights: {
321
+ accessRights: { kind: 'path', path: 'metadata.accessRights' },
322
+ fullEmbargoUntil: { kind: 'path', path: 'metadata.embargoUntil' },
323
+ fileEmbargoUntil: { kind: 'path', path: 'metadata.embargoUntil' },
324
+ reason: { kind: 'path', path: 'metadata.embargoReason' }
325
+ }
326
+ },
327
+ queue: {
328
+ publishAfterUploadDelay: typeof mapping.schedulePublishAfterUploadJob === 'string' ? mapping.schedulePublishAfterUploadJob : 'in 2 minutes',
329
+ uploadedFilesCleanupDelay: typeof mapping.scheduleUploadedFilesCleanupJob === 'string' ? mapping.scheduleUploadedFilesCleanupJob : 'in 5 minutes'
330
+ },
331
+ workflow: {
332
+ transitionRules: [],
333
+ transitionJob: {
334
+ enabled: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.enabled ?? 'false') === 'true',
335
+ namedQuery: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.namedQuery ?? ''),
336
+ targetStep: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.targetStep ?? ''),
337
+ paramMap: mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.paramMap ?? {},
338
+ figshareTargetFieldKey: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.figshareTargetFieldKey ?? ''),
339
+ figshareTargetFieldValue: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.figshareTargetFieldValue ?? ''),
340
+ username: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.username ?? ''),
341
+ userType: String(mapping.figshareScheduledTransitionRecordWorkflowFromArticlePropertiesJob?.userType ?? '')
342
+ }
343
+ },
344
+ testing: {
345
+ mode: figshareApi.testMode ? 'fixture' : 'live'
346
+ },
347
+ writeBack: {
348
+ articleId: 'metadata.figshare_article_id',
349
+ articleUrls: ['metadata.figshare_article_location'],
350
+ extraFields: []
351
+ },
352
+ migrationReport: {
353
+ brand,
354
+ unsupportedLegacyTemplates: collectLegacyTemplates(mapping)
355
+ }
356
+ };
357
+ }
358
+ function registerMigrateFigshareConfigCommand(program) {
359
+ program
360
+ .command('migrate-figshare-config')
361
+ .description('Migrate legacy Figshare config into figsharePublishing AppConfig JSON files')
362
+ .requiredOption('--figshare-api <path>', 'Path to the legacy figshareAPI config module')
363
+ .option('--figshare-api-env <path>', 'Path to the legacy figshareAPIEnv config module')
364
+ .option('--figshare-for-mapping <path>', 'Path to the legacy figshareReDBoxFORMapping config module')
365
+ .requiredOption('-o, --output <path>', 'Directory to write migrated figsharePublishing JSON files')
366
+ .option('-b, --brands <brands...>', 'Brands to generate config for', ['default'])
367
+ .action(async (options) => {
368
+ try {
369
+ const globalOptions = program.opts();
370
+ const outputDir = path.resolve(options.output);
371
+ const brands = options.brands;
372
+ const figshareApi = pickLegacyExport(loadLegacyModule(options.figshareApi), 'figshareAPI');
373
+ const figshareApiEnv = options.figshareApiEnv
374
+ ? pickLegacyExport(loadLegacyModule(options.figshareApiEnv), 'figshareAPIEnv')
375
+ : {};
376
+ const figshareForMapping = options.figshareForMapping
377
+ ? pickLegacyExport(loadLegacyModule(options.figshareForMapping), 'figshareReDBoxFORMapping')
378
+ : {};
379
+ if (!globalOptions.dryRun) {
380
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
381
+ }
382
+ const report = brands.map((brand) => {
383
+ const migrated = createMigratedFigsharePublishingConfig(brand, figshareApi, figshareApiEnv, figshareForMapping);
384
+ const outputPath = path.join(outputDir, `${brand}.figsharePublishing.json`);
385
+ if (!globalOptions.dryRun) {
386
+ fs_1.default.writeFileSync(outputPath, JSON.stringify(migrated, null, 2) + '\n', 'utf8');
387
+ }
388
+ return {
389
+ brand,
390
+ outputPath,
391
+ unsupportedLegacyTemplates: migrated.migrationReport.unsupportedLegacyTemplates
392
+ };
393
+ });
394
+ if (globalOptions.dryRun) {
395
+ console.log('[dry-run] Migration report generated; no files written.');
396
+ }
397
+ console.log(JSON.stringify(report, null, 2));
398
+ }
399
+ catch (error) {
400
+ console.error(`\n❌ Error: `, error);
401
+ process.exit(1);
402
+ }
403
+ });
404
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@researchdatabox/redbox-dev-tools",
3
- "version": "0.0.1-beta.8936",
3
+ "version": "0.0.1-beta.9018",
4
4
  "description": "ReDBox customisation dev tools - shared configuration, generators, and migration tooling",
5
5
  "type": "commonjs",
6
6
  "main": "index.js",
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "homepage": "https://github.com/redbox-mint/redbox-portal/tree/v4.5.1#readme",
46
46
  "dependencies": {
47
- "@researchdatabox/redbox-core": "0.0.1-beta.8936",
48
- "@researchdatabox/sails-ng-common": "0.0.1-beta.8936",
47
+ "@researchdatabox/redbox-core": "0.0.1-beta.9018",
48
+ "@researchdatabox/sails-ng-common": "0.0.1-beta.9018",
49
49
  "@tsconfig/node24": "24.0.1",
50
50
  "@types/chai": "5.2.3",
51
51
  "@types/lodash": "4.17.24",
@@ -73,13 +73,13 @@
73
73
  }
74
74
  },
75
75
  "overrides": {
76
+ "diff": "8.0.3",
76
77
  "mocha": {
77
78
  "serialize-javascript": "7.0.5"
78
79
  }
79
80
  },
80
81
  "publishConfig": {
81
82
  "access": "public",
82
- "registry": "https://registry.npmjs.org/",
83
- "provenance": false
83
+ "registry": "https://registry.npmjs.org/"
84
84
  }
85
85
  }
@@ -1,6 +1,6 @@
1
1
  save-exact=true
2
2
  save-prefix=
3
3
  ignore-scripts=true
4
- provenance=true
4
+ provenance=false
5
5
  registry=https://registry.npmjs.org/
6
6
  audit-level=moderate