diff --git a/.gitignore b/.gitignore index ca7e7ac763..5c7c23bb9f 100644 --- a/.gitignore +++ b/.gitignore @@ -185,3 +185,5 @@ support/raido/*.tgz angular/.angular/cache/ api/configmodels/* +api/shims/*.js + diff --git a/Dockerfile b/Dockerfile index 5053a3f033..ed8a03807e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,11 @@ FROM node:20.18-bullseye -ENV node_env production +ENV node_env=production ENV NPM_CONFIG_PREFIX=/home/node/.npm-global ENV PATH=$PATH:/home/node/.npm-global/bin -RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 -RUN chmod +x /usr/local/bin/dumb-init +ENV NODE_ENV=$node_env RUN echo "Australia/Brisbane" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata COPY --chown=node:node . /opt/redbox-portal RUN chown -R node:node /opt/redbox-portal; ls -l /opt; ls -l /opt/redbox-portal; USER node RUN echo "Permissions should be set, running as 'node' user, dumping permissions again: "; ls -l /opt; ls -l /opt/redbox-portal; -CMD NODE_ENV=$node_env node app.js +CMD ["node", "app.js"] diff --git a/api/models/AsynchProgress.js b/api/models/AsynchProgress.js index 0c8774e9eb..0f66117364 100644 --- a/api/models/AsynchProgress.js +++ b/api/models/AsynchProgress.js @@ -4,7 +4,7 @@ * @description :: Tracks Asynchrounous progress started by the portal. * @docs :: http://sailsjs.org/documentation/concepts/models-and-orm/models */ -var moment = require('moment'); +// moment removed: not used; dates stored as plain strings. module.exports = { attributes: { diff --git a/api/models/WorkspaceAsync.js b/api/models/WorkspaceAsync.js index c015443ccf..1851bc33de 100644 --- a/api/models/WorkspaceAsync.js +++ b/api/models/WorkspaceAsync.js @@ -4,7 +4,7 @@ * @description :: Tracks Asynchrounous workspace methods * @docs :: http://sailsjs.org/documentation/concepts/models-and-orm/models */ -var moment = require('moment'); +// moment removed: not used; dates stored as plain strings. module.exports = { attributes: { diff --git a/assets/styles/default-theme.scss b/assets/styles/default-theme.scss index c7041799ec..5b96db09ea 100644 --- a/assets/styles/default-theme.scss +++ b/assets/styles/default-theme.scss @@ -1,5 +1,10 @@ -$fa-font-path: "../../node_modules/font-awesome-sass/assets/fonts/font-awesome/"; -@import "~font-awesome-sass/assets/stylesheets/_font-awesome"; +// Font Awesome 6 via npm (@fortawesome/fontawesome-free) +// Ensure webpack/SASS resolves ~ to node_modules. +$fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; +@import "~@fortawesome/fontawesome-free/scss/fontawesome"; +@import "~@fortawesome/fontawesome-free/scss/solid"; +@import "~@fortawesome/fontawesome-free/scss/regular"; +@import "~@fortawesome/fontawesome-free/scss/brands"; @import "redbox-font"; diff --git a/config/bootstrap.js b/config/bootstrap.js index 67c92d743d..1b74b104b8 100644 --- a/config/bootstrap.js +++ b/config/bootstrap.js @@ -8,28 +8,28 @@ * For more information on bootstrapping your app, check out: * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.bootstrap.html */ - var Observable = require('rxjs/Observable').Observable; + const { lastValueFrom } = require('rxjs'); const schedule = require('node-schedule'); const actualBootstrap = async function() { await sails.services.translationservice.bootstrap(); sails.log.verbose("Translation service, bootstrapped."); - let defaultBrand = await sails.services.brandingservice.bootstrap().toPromise() + let defaultBrand = await lastValueFrom(sails.services.brandingservice.bootstrap()) sails.log.verbose("Branding service, bootstrapped."); - let rolesBootstrapResult = await sails.services.rolesservice.bootstrap(defaultBrand).toPromise(); + let rolesBootstrapResult = await lastValueFrom(sails.services.rolesservice.bootstrap(defaultBrand)); sails.log.verbose("Roles service, bootstrapped."); - let reportsBootstrapResult = await sails.services.reportsservice.bootstrap(sails.services.brandingservice.getDefault()).toPromise(); + let reportsBootstrapResult = await lastValueFrom(sails.services.reportsservice.bootstrap(sails.services.brandingservice.getDefault())); sails.log.verbose("Reports service, bootstrapped."); let namedQueriesBootstrapResult = await sails.services.namedqueryservice.bootstrap(sails.services.brandingservice.getDefault()); sails.log.verbose("Named Query service, bootstrapped."); // sails doesn't support 'populating' of nested associations // intentionally queried again because of nested 'users' population, couldn't be bothered with looping thru the results - let defRoles = await sails.services.rolesservice.getRolesWithBrand(sails.services.brandingservice.getDefault()).toPromise(); + let defRoles = await lastValueFrom(sails.services.rolesservice.getRolesWithBrand(sails.services.brandingservice.getDefault())); sails.log.verbose("Roles service, bootstrapped."); sails.log.verbose(defRoles); - let defUserAndDefRoles = await sails.services.usersservice.bootstrap(defRoles).toPromise(); + let defUserAndDefRoles = await lastValueFrom(sails.services.usersservice.bootstrap(defRoles)); sails.log.verbose("Pathrules service, bootstrapped."); - let pathRulesBootstrapResult = await sails.services.pathrulesservice.bootstrap(defUserAndDefRoles.defUser, defUserAndDefRoles.defRoles).toPromise(); + let pathRulesBootstrapResult = await lastValueFrom(sails.services.pathrulesservice.bootstrap(defUserAndDefRoles.defUser, defUserAndDefRoles.defRoles)); sails.log.verbose("Record types service, bootstrapped."); let recordsTypes = await sails.services.recordtypesservice.bootstrap(sails.services.brandingservice.getDefault()); sails.log.verbose("Workflowsteps service, bootstrapped."); @@ -48,7 +48,7 @@ sails.log.verbose("Forms service, bootstrapped."); - await sails.services.vocabservice.bootstrap().toPromise(); + await lastValueFrom(sails.services.vocabservice.bootstrap()); sails.log.verbose("Vocab service, bootstrapped."); // Schedule cronjobs if (sails.config.crontab.enabled) { @@ -68,7 +68,7 @@ sails.log.verbose("Cron service, bootstrapped."); // After last, because it was being triggered twice - await sails.services.workspacetypesservice.bootstrap(sails.services.brandingservice.getDefault()).toPromise(); + await lastValueFrom(sails.services.workspacetypesservice.bootstrap(sails.services.brandingservice.getDefault())); sails.log.verbose("WorkspaceTypes service, bootstrapped."); diff --git a/config/emailnotification.js b/config/emailnotification.js index 749f5f8c73..eb201fb728 100644 --- a/config/emailnotification.js +++ b/config/emailnotification.js @@ -1,28 +1,27 @@ module.exports.emailnotification = { api: { - send: {method: 'post', url: "/api/v1/messaging/emailnotification"} + send: {method: 'post', url: "/api/v1/messaging/emailnotification"} }, settings: { - enabled: true, - from: "noreply@redbox", - templateDir: "views/emailTemplates/", - //node mailer transport options. See https://nodemailer.com/smtp/ for SMTP and other transport options - serverOptions: { - host: 'smtp.ethereal.email', - port: 587, - auth: { - user: 'xxxxxxx', - pass: 'xxxxxxx' + enabled: true, + from: "noreply@redbox", + templateDir: "views/emailTemplates/", + serverOptions: { + host: 'integration-testing-email-1', + port: 1025, + secure: false, + tls: { + rejectUnauthorized: false + } } - } }, defaults: { - from: "redbox@dev", - subject: "ReDBox Notification", - format: "html" + from: "redbox@dev", + subject: "ReDBox Notification", + format: "html" }, templates: { - transferOwnerTo: {subject: 'Ownership of DMP record/s has been transferred to you', template: 'transferOwnerTo'}, - test: {subject: 'Test Email Message', template: 'test'} + transferOwnerTo: {subject: 'Ownership of DMP record/s has been transferred to you', template: 'transferOwnerTo'}, + test: {subject: 'Test Email Message', template: 'test'} } - }; +}; diff --git a/core/package-lock.json b/core/package-lock.json index 232ba41eaf..9ddaab77af 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@researchdatabox/redbox-core-types", - "version": "1.4.8", + "version": "1.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@researchdatabox/redbox-core-types", - "version": "1.4.8", + "version": "1.5.0", "license": "ISC", "dependencies": { "@tsconfig/node18": "^18.2.0" @@ -14,9 +14,8 @@ "devDependencies": { "@types/node": "^22.0.0", "lodash": "^4.17.21", - "moment": "^2.29.1", - "rxjs": "^6.6.2", - "rxjs-compat": "^6.6.7", + "luxon": "^3.7.1", + "rxjs": "^7.8.1", "stream": "0.0.3", "typescript": "^5.0.4" } @@ -53,33 +52,25 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "node_modules/luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", "dev": true, "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs-compat": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.6.7.tgz", - "integrity": "sha512-szN4fK+TqBPOFBcBcsR0g2cmTTUF/vaFEOZNuSdfU8/pGFnNmmn2u8SystYXG1QMrjOPBc6XTKHMVfENDf6hHw==", - "dev": true - }, "node_modules/stream": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.3.tgz", @@ -90,10 +81,11 @@ } }, "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "node_modules/typescript": { "version": "5.9.2", @@ -142,27 +134,21 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", "dev": true }, "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "^2.1.0" } }, - "rxjs-compat": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.6.7.tgz", - "integrity": "sha512-szN4fK+TqBPOFBcBcsR0g2cmTTUF/vaFEOZNuSdfU8/pGFnNmmn2u8SystYXG1QMrjOPBc6XTKHMVfENDf6hHw==", - "dev": true - }, "stream": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.3.tgz", @@ -173,9 +159,9 @@ } }, "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, "typescript": { diff --git a/core/package.json b/core/package.json index 3565836fd5..d0dfd21f06 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@researchdatabox/redbox-core-types", - "version": "1.4.8", + "version": "1.5.0", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -18,9 +18,8 @@ "devDependencies": { "@types/node": "^22.0.0", "lodash": "^4.17.21", - "moment": "^2.29.1", - "rxjs": "^6.6.2", - "rxjs-compat": "^6.6.7", + "rxjs": "^7.8.1", + "luxon": "^3.7.1", "stream": "0.0.3", "typescript": "^5.0.4" } diff --git a/core/src/CoreService.ts b/core/src/CoreService.ts index df589f08da..de2eb369a0 100644 --- a/core/src/CoreService.ts +++ b/core/src/CoreService.ts @@ -1,4 +1,6 @@ -import { Observable } from 'rxjs/Rx'; +import { from,bindNodeCallback, bindCallback, Observable } from 'rxjs'; + + declare var sails; // changed to a manual lodash load instead of relying on Sails global object // this enables testing of installable hooks that rely on services at load-time (i.e. index.js) @@ -26,9 +28,9 @@ export module Services.Core { */ protected getObservable(q, method='exec', type='node'): Observable { if (type == 'node') - return Observable.bindNodeCallback(q[method].bind(q))(); + return bindNodeCallback(q[method].bind(q))(); else - return Observable.bindCallback(q[method].bind(q))(); + return bindCallback(q[method].bind(q))(); } /** diff --git a/package-lock.json b/package-lock.json index 6a6596d468..cb9731d304 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,15 @@ "version": "2.1.0", "license": "GPL2", "dependencies": { + "@fortawesome/fontawesome-free": "^6.5.2", "@researchdatabox/oni-ocfl": "^1.4.0", "@researchdatabox/raido-openapi-generated-node": "file:support/raido", "@researchdatabox/redbox-core-types": "file:core", - "@researchdatabox/sails-hook-redbox-storage-mongo": "^1.4.9", + "@researchdatabox/sails-hook-redbox-storage-mongo": "^1.5.2", "@researchdatabox/sails-ng-common": "^0.2.1", "@tsconfig/node18": "^18.2.4", "agenda": "github:redbox-mint-contrib/agenda#dist", "async": "3.2.6", - "axios": "^1.10.0", "bcryptjs": "^3.0.2", "bootstrap": "^5.3.7", "captains-log": "^2.0.5", @@ -34,7 +34,6 @@ "ejs": "3.1.10", "express-session": "^1.18.2", "flat": "^6.0.1", - "font-awesome-sass": "4.7.0", "fs-extra": "^11.3.1", "genson-js": "^0.0.8", "glob": "^11.0.1", @@ -68,8 +67,7 @@ "postcss": "^8.5.6", "rc": "1.2.8", "redux": "5.0.1", - "rxjs": "6.6.2", - "rxjs-compat": "6.6.7", + "rxjs": "^7.8.1", "sails": "^1.5.15", "sails-hook-orm": "^4.0.3", "sails-hook-sockets": "^3.0.2", @@ -94,20 +92,17 @@ "@types/jquery": "^3.5.32", "@types/lodash-es": "^4.17.12", "@types/node": "^22.5.5", - "bower": "^1.8.14", "chai": "^5.2.1", "ejs-cli": "^2.2.3", - "istanbul": "^0.4.5", "mocha": "^11.7.1", "mocha-junit-reporter": "^2.2.1", "pino-test": "^1.1.0", - "supertest": "^7.1.4", - "uglify-es": "^3.3.10" + "supertest": "^7.1.4" } }, "core": { "name": "@researchdatabox/redbox-core-types", - "version": "1.4.8", + "version": "1.5.0", "license": "ISC", "dependencies": { "@tsconfig/node18": "^18.2.0" @@ -115,9 +110,8 @@ "devDependencies": { "@types/node": "^22.0.0", "lodash": "^4.17.21", - "moment": "^2.29.1", - "rxjs": "^6.6.2", - "rxjs-compat": "^6.6.7", + "luxon": "^3.7.1", + "rxjs": "^7.8.1", "stream": "0.0.3", "typescript": "^5.0.4" } @@ -410,6 +404,13 @@ "version": "14.18.63", "license": "MIT" }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.7.2", + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", + "engines": { + "node": ">=6" + } + }, "node_modules/@google-cloud/paginator": { "version": "3.0.7", "license": "Apache-2.0", @@ -707,9 +708,8 @@ }, "node_modules/@noble/hashes": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, + "license": "MIT", "engines": { "node": "^14.21.3 || >=16" }, @@ -935,9 +935,8 @@ }, "node_modules/@paralleldrive/cuid2": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", "dev": true, + "license": "MIT", "dependencies": { "@noble/hashes": "^1.1.5" } @@ -1030,12 +1029,27 @@ "link": true }, "node_modules/@researchdatabox/sails-hook-redbox-storage-mongo": { - "version": "1.4.9", + "version": "1.5.2", "license": "GPL-3.0", "dependencies": { "json2csv": "^5.0.3", "lodash": "^4.17.21", - "uuid": "8.3.0" + "uuid": "11.1.0" + }, + "peerDependencies": { + "luxon": ">=3.0.0", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@researchdatabox/sails-hook-redbox-storage-mongo/node_modules/uuid": { + "version": "11.1.0", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" } }, "node_modules/@researchdatabox/sails-ng-common": { @@ -1405,11 +1419,6 @@ "version": "4.2.2", "license": "Apache-2.0" }, - "node_modules/abbrev": { - "version": "1.0.9", - "dev": true, - "license": "ISC" - }, "node_modules/abort-controller": { "version": "3.0.0", "license": "MIT", @@ -1443,8 +1452,7 @@ }, "node_modules/acorn-import-phases": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", "engines": { "node": ">=10.13.0" }, @@ -1500,7 +1508,8 @@ }, "node_modules/ajv": { "version": "6.12.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1545,15 +1554,6 @@ "version": "1.0.0", "license": "MIT" }, - "node_modules/amdefine": { - "version": "1.0.1", - "dev": true, - "license": "BSD-3-Clause OR MIT", - "optional": true, - "engines": { - "node": ">=0.4.2" - } - }, "node_modules/anchor": { "version": "1.4.1", "license": "MIT", @@ -2071,17 +2071,6 @@ "@popperjs/core": "^2.11.8" } }, - "node_modules/bower": { - "version": "1.8.14", - "dev": true, - "license": "MIT", - "bin": { - "bower": "bin/bower" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/brace-expansion": { "version": "2.0.2", "license": "MIT", @@ -2192,8 +2181,6 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2330,8 +2317,6 @@ }, "node_modules/chai": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", "dev": true, "license": "MIT", "dependencies": { @@ -2621,9 +2606,8 @@ }, "node_modules/component-emitter": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -2653,8 +2637,6 @@ }, "node_modules/compression": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -2671,8 +2653,6 @@ }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -2680,14 +2660,10 @@ }, "node_modules/compression/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/compression/node_modules/negotiator": { "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2861,9 +2837,8 @@ }, "node_modules/cookiejar": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/copy-anything": { "version": "2.0.6", @@ -2879,7 +2854,6 @@ "version": "13.0.1", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz", "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==", - "license": "MIT", "dependencies": { "glob-parent": "^6.0.1", "normalize-path": "^3.0.0", @@ -2900,9 +2874,8 @@ }, "node_modules/core-js": { "version": "3.45.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.0.tgz", - "integrity": "sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==", "hasInstallScript": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -3254,8 +3227,7 @@ "node_modules/csv-stringify": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.6.0.tgz", - "integrity": "sha512-YW32lKOmIBgbxtu3g5SaiqWNwa/9ISQt2EcgOq0+RAIFufFp9is6tqNnKahqE5kuKvrnYAzs28r+s6pXJR8Vcw==", - "license": "MIT" + "integrity": "sha512-YW32lKOmIBgbxtu3g5SaiqWNwa/9ISQt2EcgOq0+RAIFufFp9is6tqNnKahqE5kuKvrnYAzs28r+s6pXJR8Vcw==" }, "node_modules/cycle": { "version": "1.0.3", @@ -3329,11 +3301,6 @@ "node": ">=4.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, "node_modules/default-require-extensions": { "version": "3.0.1", "license": "MIT", @@ -3404,9 +3371,8 @@ }, "node_modules/dezalgo": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, + "license": "ISC", "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -3414,9 +3380,8 @@ }, "node_modules/dezalgo/node_modules/asap": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/diff": { "version": "7.0.0", @@ -3495,8 +3460,7 @@ }, "node_modules/dotenv": { "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -3875,27 +3839,6 @@ "node": ">=0.8.0" } }, - "node_modules/escodegen": { - "version": "1.8.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.12.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "license": "BSD-2-Clause", @@ -3914,18 +3857,6 @@ "node": ">=4.0" } }, - "node_modules/esprima": { - "version": "2.7.3", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "license": "BSD-2-Clause", @@ -3943,21 +3874,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "1.9.3", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/etag": { "version": "1.8.1", "license": "MIT", @@ -4043,8 +3959,6 @@ }, "node_modules/express-session": { "version": "1.18.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "license": "MIT", "dependencies": { "cookie": "0.7.2", @@ -4178,12 +4092,8 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-redact": { "version": "3.5.0", @@ -4194,9 +4104,8 @@ }, "node_modules/fast-safe-stringify": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-text-encoding": { "version": "1.0.6", @@ -4409,10 +4318,6 @@ } } }, - "node_modules/font-awesome-sass": { - "version": "4.7.0", - "license": "MIT" - }, "node_modules/for-each": { "version": "0.3.5", "license": "MIT", @@ -4442,8 +4347,7 @@ }, "node_modules/form-data": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4457,9 +4361,8 @@ }, "node_modules/formidable": { "version": "3.5.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", - "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, + "license": "MIT", "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", @@ -4512,7 +4415,6 @@ "version": "11.3.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -4849,44 +4751,19 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/handlebars": { - "version": "4.7.8", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/har-schema": { "version": "2.0.0", - "license": "ISC", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "engines": { "node": ">=4" } }, "node_modules/har-validator": { "version": "5.1.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -5072,7 +4949,6 @@ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" } ], - "license": "MIT", "dependencies": { "@babel/runtime": "^7.27.6" }, @@ -5411,30 +5287,6 @@ "version": "0.1.2", "license": "MIT" }, - "node_modules/istanbul": { - "version": "0.4.5", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "istanbul": "lib/cli.js" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "license": "BSD-3-Clause", @@ -5580,87 +5432,6 @@ "node": ">=8" } }, - "node_modules/istanbul/node_modules/async": { - "version": "1.5.2", - "dev": true, - "license": "MIT" - }, - "node_modules/istanbul/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/istanbul/node_modules/glob": { - "version": "5.0.15", - "dev": true, - "license": "ISC", - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/istanbul/node_modules/has-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/istanbul/node_modules/mkdirp": { - "version": "0.5.6", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/istanbul/node_modules/supports-color": { - "version": "3.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/istanbul/node_modules/which": { - "version": "1.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/jackspeak": { "version": "4.1.1", "license": "BlueOak-1.0.0", @@ -5939,7 +5710,8 @@ }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json2csv": { "version": "5.0.7", @@ -6472,8 +6244,6 @@ }, "node_modules/less": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/less/-/less-4.4.0.tgz", - "integrity": "sha512-kdTwsyRuncDfjEs0DlRILWNvxhDG/Zij4YLO4TMJgDLW+8OzpfkdPnRgrsRuY1o+oaxJGWsps5f/RVBgGmmN0w==", "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", @@ -6528,18 +6298,6 @@ "node": ">=0.10.0" } }, - "node_modules/levn": { - "version": "0.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lie": { "version": "3.3.0", "license": "MIT", @@ -6804,8 +6562,6 @@ }, "node_modules/luxon": { "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", "license": "MIT", "engines": { "node": ">=12" @@ -7089,7 +6845,6 @@ "version": "2.9.4", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz", "integrity": "sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==", - "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -7149,9 +6904,8 @@ }, "node_modules/mocha": { "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", "dev": true, + "license": "MIT", "dependencies": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", @@ -7348,15 +7102,14 @@ }, "node_modules/moment": { "version": "2.30.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } }, "node_modules/mongodb": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", - "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", @@ -7551,23 +7304,11 @@ }, "node_modules/nodemailer": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", - "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", + "license": "MIT-0", "engines": { "node": ">=6.0.0" } }, - "node_modules/nopt": { - "version": "3.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "license": "MIT", @@ -7905,8 +7646,6 @@ }, "node_modules/on-headers": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -8036,22 +7775,6 @@ "node": ">=4" } }, - "node_modules/optionator": { - "version": "0.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "license": "MIT", @@ -8291,7 +8014,6 @@ "version": "9.9.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz", "integrity": "sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==", - "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", @@ -8318,8 +8040,7 @@ }, "node_modules/pino-logfmt": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pino-logfmt/-/pino-logfmt-1.0.1.tgz", - "integrity": "sha512-H81lcGM7Abl/PijeZ9jmXMi9Ul1AUDfQDJuC5/fSdB5f3oXFkJSdah2U40alj5gRmrpyXigUdfg/A33kQHbLKQ==", + "license": "MIT", "dependencies": { "case-anything": "^2.1.13", "dateformat": "^5.0.3", @@ -8865,13 +8586,6 @@ "version": "4.2.0", "license": "MIT" }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "license": "MIT" @@ -8991,9 +8705,8 @@ }, "node_modules/qs": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" }, @@ -9409,23 +9122,12 @@ } }, "node_modules/rxjs": { - "version": "6.6.2", + "version": "7.8.2", "license": "Apache-2.0", "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs-compat": { - "version": "6.6.7", - "license": "Apache-2.0" - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -9472,8 +9174,6 @@ }, "node_modules/sails": { "version": "1.5.15", - "resolved": "https://registry.npmjs.org/sails/-/sails-1.5.15.tgz", - "integrity": "sha512-/FVDu1mLCQJCsCm7Tzc3sgFc06PLwxaGLn7p7+wNV13Y+Fogny5NZSn79AEU1b3xNbCRPxiRZYZOVjHjURFd9A==", "license": "MIT", "dependencies": { "@sailshq/csurf": "1.11.1", @@ -9869,7 +9569,6 @@ "version": "1.90.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", - "license": "MIT", "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -10572,17 +10271,6 @@ "version": "1.3.0", "license": "MIT" }, - "node_modules/source-map": { - "version": "0.2.0", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "license": "BSD-3-Clause", @@ -10884,9 +10572,8 @@ }, "node_modules/superagent": { "version": "10.2.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", - "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, + "license": "MIT", "dependencies": { "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", @@ -10904,9 +10591,8 @@ }, "node_modules/superagent/node_modules/mime": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -10916,9 +10602,8 @@ }, "node_modules/supertest": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", - "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, + "license": "MIT", "dependencies": { "methods": "^1.1.2", "superagent": "^10.2.3" @@ -11328,17 +11013,6 @@ "node": ">=10.0" } }, - "node_modules/type-check": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.1.0", "license": "MIT", @@ -11487,46 +11161,6 @@ "node": ">=4.2.0" } }, - "node_modules/uglify-es": { - "version": "3.3.10", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "commander": "~2.14.1", - "source-map": "~0.6.1" - }, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uglify-es/node_modules/commander": { - "version": "2.14.1", - "dev": true, - "license": "MIT" - }, - "node_modules/uglify-es/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/uid-safe": { "version": "2.1.5", "license": "MIT", @@ -11650,7 +11284,8 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dependencies": { "punycode": "^2.1.0" } @@ -11874,7 +11509,6 @@ "version": "5.101.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.1.tgz", "integrity": "sha512-rHY3vHXRbkSfhG6fH8zYQdth/BtDgXXuR2pHF++1f/EBkI8zkgM5XWfsC3BvOoW9pr1CvZ1qQCxhCEsbNgT50g==", - "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -11920,8 +11554,7 @@ }, "node_modules/webpack-sources": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -12120,17 +11753,10 @@ "betterknown": "^1.0.4" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wordwrap": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, "node_modules/workerpool": { "version": "9.3.2", @@ -12521,11 +12147,6 @@ "dev": true, "license": "MIT" }, - "support/raido/node_modules/@types/node": { - "version": "18.11.18", - "dev": true, - "license": "MIT" - }, "support/raido/node_modules/@types/request": { "version": "2.48.8", "dev": true, @@ -12542,10 +12163,6 @@ "dev": true, "license": "MIT" }, - "support/raido/node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, "support/raido/node_modules/axios": { "version": "1.11.0", "license": "MIT", @@ -12569,101 +12186,6 @@ "node": ">= 6" } }, - "support/raido/node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/combined-stream": { - "version": "1.0.8", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "support/raido/node_modules/delayed-stream": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "support/raido/node_modules/dunder-proto": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/es-define-property": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/es-errors": { - "version": "1.3.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/es-object-atoms": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/es-set-tostringtag": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/follow-redirects": { - "version": "1.15.9", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "support/raido/node_modules/form-data": { "version": "2.5.1", "dev": true, @@ -12677,117 +12199,6 @@ "node": ">= 0.12" } }, - "support/raido/node_modules/function-bind": { - "version": "1.1.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "support/raido/node_modules/get-intrinsic": { - "version": "1.3.0", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "support/raido/node_modules/get-proto": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/gopd": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "support/raido/node_modules/has-symbols": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "support/raido/node_modules/has-tostringtag": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "support/raido/node_modules/hasown": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/math-intrinsics": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "support/raido/node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "support/raido/node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "support/raido/node_modules/proxy-from-env": { - "version": "1.1.0", - "license": "MIT" - }, "support/raido/node_modules/typescript": { "version": "4.9.4", "dev": true, diff --git a/package.json b/package.json index 67a8b1270a..4dcf72541c 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "ReDBox 2 Portal", "keywords": [], "dependencies": { + "@fortawesome/fontawesome-free": "^6.5.2", "@researchdatabox/oni-ocfl": "^1.4.0", "@researchdatabox/raido-openapi-generated-node": "file:support/raido", "@researchdatabox/redbox-core-types": "file:core", - "@researchdatabox/sails-hook-redbox-storage-mongo": "^1.4.9", + "@researchdatabox/sails-hook-redbox-storage-mongo": "^1.5.2", "@researchdatabox/sails-ng-common": "^0.2.1", "@tsconfig/node18": "^18.2.4", "agenda": "github:redbox-mint-contrib/agenda#dist", "async": "3.2.6", - "axios": "^1.10.0", "bcryptjs": "^3.0.2", "bootstrap": "^5.3.7", "captains-log": "^2.0.5", @@ -29,7 +29,6 @@ "ejs": "3.1.10", "express-session": "^1.18.2", "flat": "^6.0.1", - "font-awesome-sass": "4.7.0", "fs-extra": "^11.3.1", "genson-js": "^0.0.8", "glob": "^11.0.1", @@ -63,8 +62,7 @@ "postcss": "^8.5.6", "rc": "1.2.8", "redux": "5.0.1", - "rxjs": "6.6.2", - "rxjs-compat": "6.6.7", + "rxjs": "^7.8.1", "sails": "^1.5.15", "sails-hook-orm": "^4.0.3", "sails-hook-sockets": "^3.0.2", @@ -115,15 +113,12 @@ "@types/jquery": "^3.5.32", "@types/lodash-es": "^4.17.12", "@types/node": "^22.5.5", - "bower": "^1.8.14", "chai": "^5.2.1", "ejs-cli": "^2.2.3", - "istanbul": "^0.4.5", "mocha": "^11.7.1", "mocha-junit-reporter": "^2.2.1", "pino-test": "^1.1.0", - "supertest": "^7.1.4", - "uglify-es": "^3.3.10" + "supertest": "^7.1.4" }, "nyc": { "check-coverage": false, diff --git a/support/deployment-examples/docker/docker-compose.yml b/support/deployment-examples/docker/docker-compose.yml index c3576d6127..837bfe61be 100644 --- a/support/deployment-examples/docker/docker-compose.yml +++ b/support/deployment-examples/docker/docker-compose.yml @@ -18,6 +18,7 @@ services: networks: main: aliases: [rbportal] + init: true entrypoint: '/bin/bash -c "cd /opt/redbox-portal && node app.js"' redbox: image: 'qcifengineering/redbox:2.x' diff --git a/support/development/docker-compose.yml b/support/development/docker-compose.yml index a701bdb265..23b00feb17 100644 --- a/support/development/docker-compose.yml +++ b/support/development/docker-compose.yml @@ -40,6 +40,7 @@ services: main: aliases: - rbportal + init: true entrypoint: /bin/bash -c "cd /opt/redbox-portal && node --inspect=0.0.0.0:9876 app.js" mongodb: #image: mvertes/alpine-mongo:latest diff --git a/support/integration-testing/docker-compose.bruno.yml b/support/integration-testing/docker-compose.bruno.yml index 792ac308a1..4ae6846fa2 100755 --- a/support/integration-testing/docker-compose.bruno.yml +++ b/support/integration-testing/docker-compose.bruno.yml @@ -67,6 +67,7 @@ services: aliases: - redboxportal # add 'node --inspect=0.0.0.0:9876' to entrypoint for debugging + init: true entrypoint: >- /bin/bash -c "cd /opt/redbox-portal && ./support/integration-testing/prepare-guest.sh /opt/redbox-portal && diff --git a/support/integration-testing/docker-compose.mocha.yml b/support/integration-testing/docker-compose.mocha.yml index 7b33a4d1e9..4cb2527fdd 100755 --- a/support/integration-testing/docker-compose.mocha.yml +++ b/support/integration-testing/docker-compose.mocha.yml @@ -43,6 +43,7 @@ services: aliases: - redboxportal # add 'node --inspect=0.0.0.0:9876' to entrypoint for debugging + init: true entrypoint: >- /bin/bash -c "cd /opt/redbox-portal && ./support/integration-testing/prepare-guest.sh /opt/redbox-portal && diff --git a/support/integration-testing/docker-compose.yml b/support/integration-testing/docker-compose.yml index e7212a6094..e7c352c321 100644 --- a/support/integration-testing/docker-compose.yml +++ b/support/integration-testing/docker-compose.yml @@ -3,6 +3,7 @@ networks: main: services: redboxportal: + image: qcifengineering/redbox-portal:latest ports: - "1500:1500" @@ -24,6 +25,7 @@ services: main: aliases: - rbportal + init: true entrypoint: /bin/bash -c "cd /opt/redbox-portal && node app.js" mongodb: image: mvertes/alpine-mongo:latest diff --git a/support/integration-testing/prepare-guest.sh b/support/integration-testing/prepare-guest.sh old mode 100644 new mode 100755 diff --git a/support/raido/package-lock.json b/support/raido/package-lock.json index f7905f03cb..090bf1f2ee 100644 --- a/support/raido/package-lock.json +++ b/support/raido/package-lock.json @@ -18,21 +18,23 @@ }, "node_modules/@types/caseless": { "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", + "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } }, "node_modules/@types/request": { "version": "2.48.8", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", - "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/caseless": "*", "@types/node": "*", @@ -42,19 +44,18 @@ }, "node_modules/@types/tough-cookie": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", - "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/axios": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -63,8 +64,7 @@ }, "node_modules/axios/node_modules/form-data": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -80,6 +80,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -92,6 +93,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -103,6 +105,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -111,6 +114,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -124,6 +128,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -132,6 +137,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -140,6 +146,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -151,6 +158,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -162,15 +170,16 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -182,9 +191,8 @@ }, "node_modules/form-data": { "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -198,6 +206,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -206,6 +215,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -229,6 +239,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -241,6 +252,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -252,6 +264,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -263,6 +276,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -277,6 +291,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -288,6 +303,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -296,6 +312,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -304,6 +321,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -314,13 +332,13 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/typescript": { "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -328,6 +346,13 @@ "engines": { "node": ">=4.2.0" } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" } } } diff --git a/test/bootstrap.test.js b/test/bootstrap.test.js index b4a28d52ce..f3daeca1f7 100644 --- a/test/bootstrap.test.js +++ b/test/bootstrap.test.js @@ -1,8 +1,9 @@ var sails = require('sails'); var _ = require('lodash'); +const { DateTime } = require('luxon'); -global.moment = require('moment'); - +// Expose Luxon DateTime for tests needing date utilities +global.DateTime = DateTime; before(function (done) { diff --git a/test/unit/services/NamedQueryService.test.js b/test/unit/services/NamedQueryService.test.js index 3505beaa67..26467d2461 100644 --- a/test/unit/services/NamedQueryService.test.js +++ b/test/unit/services/NamedQueryService.test.js @@ -70,10 +70,10 @@ describe('The Named Query Service', function () { expect(namedQueryConfig.mongoQuery.defaultIsoDatePath).to.deep.equal({ "=>": '2024-12-18T00:00:00.000Z'}); expect(namedQueryConfig.mongoQuery.stringPath).to.deep.equal({contains: 'defaultString'}); - const daysDateString = namedQueryConfig.mongoQuery.daysDatePath["<="] - expect(daysDateString).to.be.a('string'); - let yesterdayIsoString = moment().subtract(1, 'days').format('YYYY-MM-DD'); - expect(daysDateString).to.contain(yesterdayIsoString); + const daysDateString = namedQueryConfig.mongoQuery.daysDatePath["<="] + expect(daysDateString).to.be.a('string'); + const yesterdayIsoString = DateTime.now().minus({ days: 1 }).toFormat('yyyy-LL-dd'); + expect(daysDateString).to.contain(yesterdayIsoString); done() }) @@ -148,10 +148,10 @@ describe('The Named Query Service', function () { expect(namedQueryConfig.mongoQuery.defaultIsoDatePath).to.deep.equal({ "=>": defaultIsoDateParamValue}); expect(namedQueryConfig.mongoQuery.stringPath).to.deep.equal({contains: stringParamValue}); - const daysDateString = namedQueryConfig.mongoQuery.daysDatePath["<="] - expect(daysDateString).to.be.a('string'); - let dayBeforeYesterdayIsoString = moment().subtract(2, 'days').format('YYYY-MM-DD'); - expect(daysDateString).to.contain(dayBeforeYesterdayIsoString); + const daysDateString = namedQueryConfig.mongoQuery.daysDatePath["<="] + expect(daysDateString).to.be.a('string'); + const dayBeforeYesterdayIsoString = DateTime.now().minus({ days: 2 }).toFormat('yyyy-LL-dd'); + expect(daysDateString).to.contain(dayBeforeYesterdayIsoString); done() }) diff --git a/tsconfig.json b/tsconfig.json index 5b2d8dc6f0..873f6ca634 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,9 @@ "rootDir": "./typescript", "strict": false, "inlineSourceMap": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "esModuleInterop": true, + "skipLibCheck": true }, "exclude": [ "node_modules/**", diff --git a/typescript/api/controllers/ActionController.ts b/typescript/api/controllers/ActionController.ts index c58f7271bd..1838715d6b 100644 --- a/typescript/api/controllers/ActionController.ts +++ b/typescript/api/controllers/ActionController.ts @@ -21,7 +21,7 @@ declare var module; declare var sails; declare var _; -import { Observable } from 'rxjs/Rx'; + declare function require(name:string); /** diff --git a/typescript/api/controllers/AdminController.ts b/typescript/api/controllers/AdminController.ts index 1ebb633912..f19d72fe6c 100644 --- a/typescript/api/controllers/AdminController.ts +++ b/typescript/api/controllers/AdminController.ts @@ -17,15 +17,17 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// + declare var module; declare var sails; declare var _; -import { Observable } from 'rxjs/Rx'; +import { of } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; + import { v4 as uuidv4 } from 'uuid'; declare var BrandingService, RolesService, UsersService; -import { BrandingModel, Controllers as controllers} from '@researchdatabox/redbox-core-types'; +import { Controllers as controllers, BrandingModel } from '@researchdatabox/redbox-core-types'; export module Controllers { /** @@ -73,7 +75,7 @@ export module Controllers { public getUsers(req, res) { var pageData:any = {}; - var users = UsersService.getUsers().flatMap(users => { + var users = UsersService.getUsers().pipe(flatMap(users => { _.map(users, (user) => { if (_.isEmpty(_.find(sails.config.auth.hiddenUsers, (hideUser) => { return hideUser == user.name }))) { // not hidden, adding to view data... @@ -87,8 +89,8 @@ export module Controllers { pageData.users.push(user); } }); - return Observable.of(pageData); - }) + return of(pageData); + })) .subscribe(pageData => { this.ajaxOk(req, res, null, pageData.users); }); @@ -98,7 +100,7 @@ export module Controllers { // basic roles page: view all users and their roles var pageData:any = {}; var brand:BrandingModel = BrandingService.getBrand(req.session.branding); - var roles = RolesService.getRolesWithBrand(brand).flatMap(roles => { + var roles = RolesService.getRolesWithBrand(brand).pipe(flatMap(roles => { _.map(roles, (role) => { if (_.isEmpty(_.find(sails.config.auth.hiddenRoles, (hideRole) => { return hideRole == role.name }))) { // not hidden, adding to view data... @@ -108,8 +110,8 @@ export module Controllers { pageData.roles.push(role); } }); - return Observable.of(pageData); - }) + return of(pageData); + })) .subscribe(pageData => { this.ajaxOk(req, res, null, pageData.roles); }); diff --git a/typescript/api/controllers/AsynchController.ts b/typescript/api/controllers/AsynchController.ts index c04df64565..4b7de9caf0 100644 --- a/typescript/api/controllers/AsynchController.ts +++ b/typescript/api/controllers/AsynchController.ts @@ -21,7 +21,7 @@ declare var module; declare var sails; declare var _; -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; declare function require(name:string); declare var AsynchsService, VocabService, BrandingService; diff --git a/typescript/api/controllers/BrandingController.ts b/typescript/api/controllers/BrandingController.ts index 1ecfeb8f19..56f5cb2007 100644 --- a/typescript/api/controllers/BrandingController.ts +++ b/typescript/api/controllers/BrandingController.ts @@ -2,8 +2,7 @@ import { Controllers as controllers} from '@researchdatabox/redbox-core-types'; import skipperGridFs from "skipper-gridfs"; import {Model} from "sails"; import {Sails} from "sails"; -import {Observable} from 'rxjs/Rx'; -import 'rxjs/add/operator/toPromise'; +import {Observable} from 'rxjs'; import * as ejs from 'ejs'; import * as fs from 'graceful-fs'; import path from "path"; diff --git a/typescript/api/controllers/DynamicAssetController.ts b/typescript/api/controllers/DynamicAssetController.ts index 93064c6fa2..70b95faf5b 100644 --- a/typescript/api/controllers/DynamicAssetController.ts +++ b/typescript/api/controllers/DynamicAssetController.ts @@ -20,7 +20,7 @@ // declare var module; declare var sails; -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; /** * Package that contains all Controllers. */ diff --git a/typescript/api/controllers/ExportController.ts b/typescript/api/controllers/ExportController.ts index a6ddf2cec8..34f70b8404 100644 --- a/typescript/api/controllers/ExportController.ts +++ b/typescript/api/controllers/ExportController.ts @@ -21,7 +21,7 @@ declare var module; declare var sails; declare var _; -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; declare var RecordsService, DashboardService, BrandingService, TranslationService; import { default as util } from 'util'; import { default as stream } from 'stream'; diff --git a/typescript/api/controllers/RecordAuditController.ts b/typescript/api/controllers/RecordAuditController.ts index 6baf01170b..fd61bb7dd7 100644 --- a/typescript/api/controllers/RecordAuditController.ts +++ b/typescript/api/controllers/RecordAuditController.ts @@ -26,7 +26,7 @@ declare var RecordsService; * Package that contains all Controllers. */ import { Controllers as controllers} from '@researchdatabox/redbox-core-types'; -import { default as moment } from 'moment'; +import moment from '../shims/momentShim'; import { orderBy } from 'lodash'; export module Controllers { @@ -61,7 +61,7 @@ export module Controllers { RecordsService.getRecordAudit(params).then(records => { let orderedRecords = orderBy(records, ['updatedAt'], ['desc']); req.options.locals["records"] = orderedRecords; - req.options.locals["moment"] = moment; + req.options.locals["moment"] = moment; return this.sendView(req, res, 'record/viewAudit'); }); } diff --git a/typescript/api/controllers/RecordController.ts b/typescript/api/controllers/RecordController.ts index 4668be1d2a..ad94d1d783 100644 --- a/typescript/api/controllers/RecordController.ts +++ b/typescript/api/controllers/RecordController.ts @@ -21,16 +21,15 @@ declare var module; declare var sails; import { - Observable -} from 'rxjs/Rx'; + Observable, of, from, throwError, firstValueFrom +} from 'rxjs'; +import { mergeMap as flatMap, map } from 'rxjs/operators'; import { StorageServiceResponse, RecordTypeResponseModel, - DashboardTypeResponseModel, - RecordTypeModel, - BrandingModel + DashboardTypeResponseModel } from '@researchdatabox/redbox-core-types'; -import { default as moment } from 'moment'; +import { DateTime } from 'luxon'; import * as tus from 'tus-node-server'; import * as fs from 'fs'; import * as url from 'url'; @@ -43,7 +42,7 @@ declare var DashboardTypesService; /** * Package that contains all Controllers. */ -import { Controllers as controllers, DatastreamService, RecordsService, SearchService } from '@researchdatabox/redbox-core-types'; +import { Controllers as controllers, DatastreamService, RecordsService, SearchService, BrandingModel, RecordTypeModel } from '@researchdatabox/redbox-core-types'; export module Controllers { /** @@ -124,7 +123,7 @@ export module Controllers { if(_.isEmpty(record)) { return res.notFound(); } - let hasViewAccess = await this.hasViewAccess(brand, req.user, record).toPromise() + let hasViewAccess = await firstValueFrom(this.hasViewAccess(brand, req.user, record)) if (hasViewAccess) { return res.json(record.metadata); } else { @@ -188,10 +187,10 @@ export module Controllers { return res.serverError(); }); } else { - Observable.fromPromise(this.recordsService.getMeta(oid)).flatMap(record => { + from(this.recordsService.getMeta(oid)).pipe(flatMap(record => { const formName = record.metaMetadata.form; return FormsService.getFormByName(formName, true); - }).subscribe(form => { + })).subscribe(form => { sails.log.debug(form); if (form['customAngularApp'] != null) { appSelector = form['customAngularApp']['appSelector']; @@ -222,13 +221,13 @@ export module Controllers { protected hasEditAccess(brand, user, currentRec): Observable { sails.log.verbose("Current Record: "); sails.log.verbose(currentRec); - return Observable.of(this.recordsService.hasEditAccess(brand, user, user.roles, currentRec)); + return of(this.recordsService.hasEditAccess(brand, user, user.roles, currentRec)); } protected hasViewAccess(brand, user, currentRec) { sails.log.verbose("Current Record: "); sails.log.verbose(currentRec); - return Observable.of(this.recordsService.hasViewAccess(brand, user, user.roles, currentRec)); + return of(this.recordsService.hasViewAccess(brand, user, user.roles, currentRec)); } public async getForm(req, res) { @@ -241,7 +240,7 @@ export module Controllers { try { if (_.isEmpty(oid)) { //find form to create a record - let form = await FormsService.getFormByStartingWorkflowStep(brand, recordType, editMode).toPromise(); + let form: any = await firstValueFrom(FormsService.getFormByStartingWorkflowStep(brand, recordType, editMode)); if (_.isEmpty(form)) { return this.ajaxFail(req, res, null, {message: `Error, getting form for record type: ${recordType}`}); } @@ -261,7 +260,7 @@ export module Controllers { if (editMode) { //find form to edit a record - let hasEditAccess = await this.hasEditAccess(brand, req.user, currentRec).toPromise(); + let hasEditAccess = await firstValueFrom(this.hasEditAccess(brand, req.user, currentRec)); if (!hasEditAccess) { return this.ajaxFail(req, res, null, {message: TranslationService.t('edit-error-no-permissions')}); } @@ -271,7 +270,7 @@ export module Controllers { } } else { //find form to view a record - let hasViewAccess = await this.hasViewAccess(brand, req.user, currentRec).toPromise(); + let hasViewAccess = await firstValueFrom(this.hasViewAccess(brand, req.user, currentRec)); if (!hasViewAccess) { return this.ajaxFail(req, res, null, {message: TranslationService.t('view-error-no-permissions')}); } @@ -279,7 +278,7 @@ export module Controllers { if (_.isEmpty(form)) { return this.ajaxFail(req, res, null, {message: `Error, getting form ${formParam} for OID: ${oid}`}); } - let hasEditAccess = await this.hasEditAccess(brand, req.user, currentRec).toPromise(); + let hasEditAccess = await firstValueFrom(this.hasEditAccess(brand, req.user, currentRec)); FormsService.filterFieldsHasEditAccess(form.fields, hasEditAccess); } @@ -324,7 +323,7 @@ export module Controllers { }; record.metadata = metadata; - let recordType = await RecordTypesService.get(brand, recType).toPromise(); + let recordType = await firstValueFrom(RecordTypesService.get(brand, recType)); const user = req.user; sails.log.verbose(`RecordController - createRecord - enter`); @@ -347,14 +346,14 @@ export module Controllers { const oid = req.param('oid'); const user = req.user; let message = null; - let currentRec = await this.getRecord(oid).toPromise(); + let currentRec = await firstValueFrom(this.getRecord(oid)); if(!_.isEmpty(brand)) { - let hasEditAccess = await this.hasEditAccess(brand, user, currentRec).toPromise(); + let hasEditAccess = await firstValueFrom(this.hasEditAccess(brand, user, currentRec)); if (hasEditAccess) { - let recordType = await RecordTypesService.get(brand, currentRec.metaMetadata.type).toPromise(); + let recordType = await firstValueFrom(RecordTypesService.get(brand, currentRec.metaMetadata.type)); let response = await this.recordsService.delete(oid, false, currentRec, recordType, user); @@ -458,15 +457,15 @@ export module Controllers { const metadata = req.body; sails.log.verbose(`RecordController - updateInternal - enter`); - let currentRec = await this.getRecord(oid).toPromise(); - let hasEditAccess = await this.hasEditAccess(brand, user, currentRec).toPromise(); + let currentRec = await firstValueFrom(this.getRecord(oid)); + let hasEditAccess = await firstValueFrom(this.hasEditAccess(brand, user, currentRec)); if (!hasEditAccess) { return res.forbidden(); } - let recordType = await RecordTypesService.get(brand, currentRec.metaMetadata.type).toPromise(); + let recordType = await firstValueFrom(RecordTypesService.get(brand, currentRec.metaMetadata.type)); let nextStepResp = null; if (targetStep) { - nextStepResp = await WorkflowStepsService.get(recordType, targetStep).toPromise(); + nextStepResp = await firstValueFrom(WorkflowStepsService.get(recordType, targetStep)); } let response; @@ -497,7 +496,7 @@ export module Controllers { protected saveAuthorization(brand, oid, currentRec, authorization, user): Observable { let editAccessResp: Observable = this.hasEditAccess(brand, user, currentRec); return editAccessResp - .map(hasEditAccess => { + .pipe(map(hasEditAccess => { if (hasEditAccess) { currentRec.authorization = authorization; return this.updateAuthorization(brand, oid, currentRec, user); @@ -507,35 +506,35 @@ export module Controllers { message: "Not authorized to edit" }; } - }); + })); } protected getRecord(oid) { - return Observable.fromPromise(this.recordsService.getMeta(oid)).flatMap(currentRec => { + return from(this.recordsService.getMeta(oid)).pipe(flatMap(currentRec => { if (_.isEmpty(currentRec)) { - return Observable.throw(new Error(`Failed to update meta, cannot find existing record with oid: ${oid}`)); + return throwError(new Error(`Failed to update meta, cannot find existing record with oid: ${oid}`)); } - return Observable.of(currentRec); - }); + return of(currentRec); + })); } //TODO: check if this is deprecated? protected updateMetadata(brand, oid, currentRec, user) { if (currentRec.metaMetadata.brandId != brand.id) { - return Observable.throw(new Error(`Failed to update meta, brand's don't match: ${currentRec.metaMetadata.brandId} != ${brand.id}, with oid: ${oid}`)); + return throwError(new Error(`Failed to update meta, brand's don't match: ${currentRec.metaMetadata.brandId} != ${brand.id}, with oid: ${oid}`)); } currentRec.metaMetadata.lastSavedBy = user.username; - currentRec.metaMetadata.lastSaveDate = moment().format(); + currentRec.metaMetadata.lastSaveDate = DateTime.local().toISO(); sails.log.verbose(`Calling record service...`); sails.log.verbose(currentRec); - return Observable.fromPromise(this.recordsService.updateMeta(brand, oid, currentRec, user)); + return from(this.recordsService.updateMeta(brand, oid, currentRec, user)); } protected updateAuthorization(brand, oid, currentRec, user) { if (currentRec.metaMetadata.brandId != brand.id) { - return Observable.throw(new Error(`Failed to update meta, brand's don't match: ${currentRec.metaMetadata.brandId} != ${brand.id}, with oid: ${oid}`)); + return throwError(new Error(`Failed to update meta, brand's don't match: ${currentRec.metaMetadata.brandId} != ${brand.id}, with oid: ${oid}`)); } - return Observable.fromPromise(this.recordsService.updateMeta(brand, oid, currentRec, user)); + return from(this.recordsService.updateMeta(brand, oid, currentRec, user)); } public stepTo(req, res) { @@ -544,18 +543,18 @@ export module Controllers { const oid = req.param('oid'); const targetStep = req.param('targetStep'); let origRecord = null; - return this.getRecord(oid).flatMap(currentRec => { + return this.getRecord(oid).pipe(flatMap(currentRec => { origRecord = _.cloneDeep(currentRec); return this.hasEditAccess(brand, req.user, currentRec) - .flatMap(hasEditAccess => { + .pipe(flatMap(hasEditAccess => { if (!hasEditAccess) { - return Observable.throw(new Error(TranslationService.t('edit-error-no-permissions'))); + return throwError(new Error(TranslationService.t('edit-error-no-permissions'))); } return RecordTypesService.get(brand, origRecord.metaMetadata.type); }) - .flatMap(recType => { + ,flatMap(recType => { return WorkflowStepsService.get(recType, targetStep) - .flatMap(nextStep => { + .pipe(flatMap(nextStep => { currentRec.metadata = metadata; sails.log.verbose("Current rec:"); sails.log.verbose(currentRec); @@ -563,11 +562,11 @@ export module Controllers { sails.log.verbose(nextStep); this.recordsService.setWorkflowStepRelatedMetadata(currentRec, nextStep); return this.updateMetadata(brand, oid, currentRec, req.user); - }); - }) - }) + })); + })) + })) .subscribe(response => { - let responseValue: Observable = response; + let responseValue: any= response; return responseValue.subscribe(response => { sails.log.error(response); if (response && response.isSuccessful()) { @@ -586,8 +585,8 @@ export module Controllers { protected async mergeFields(req, res, fields, requiredFieldIndicator, type, currentRec) { - let recordType = await RecordTypesService.get(BrandingService.getBrand(req.session.branding), type).toPromise(); - let workflowSteps = await WorkflowStepsService.getAllForRecordType(recordType).toPromise(); + let recordType = await firstValueFrom(RecordTypesService.get(BrandingService.getBrand(req.session.branding), type)); + let workflowSteps = await firstValueFrom(WorkflowStepsService.getAllForRecordType(recordType)); this.mergeFieldsSync(req, res, fields, requiredFieldIndicator, currentRec, workflowSteps); return fields; } @@ -787,7 +786,7 @@ export module Controllers { // If a record type is set, fetch from the configuration what core it's being sent from if(type != null) { - let recordType:RecordTypeModel = await RecordTypesService.get(brand, type).toPromise(); + let recordType:RecordTypeModel = await firstValueFrom(RecordTypesService.get(brand, type)); core = recordType.searchCore; } if (_.isEmpty(rows)) { @@ -949,14 +948,14 @@ export module Controllers { return; } const that = this; - const currentRec = await this.getRecord(oid).toPromise(); + const currentRec = await firstValueFrom(this.getRecord(oid)); if (method == 'get') { - const hasViewAccess = await this.hasViewAccess(brand, req.user, currentRec).toPromise(); + const hasViewAccess = await firstValueFrom(this.hasViewAccess(brand, req.user, currentRec)); if (!hasViewAccess) { sails.log.error("Error: edit error no permissions in do attachment."); - return Observable.throwError(new Error(TranslationService.t('edit-error-no-permissions'))); + return throwError(new Error(TranslationService.t('edit-error-no-permissions'))); } // check if this attachId exists in the record let found = null; @@ -973,7 +972,7 @@ export module Controllers { }); if (!found) { sails.log.verbose("Error: Attachment not found in do attachment."); - return Observable.throwError(new Error(TranslationService.t('attachment-not-found'))); + return throwError(new Error(TranslationService.t('attachment-not-found'))); } let mimeType = found.mimeType; if (_.isEmpty(mimeType)) { @@ -997,7 +996,7 @@ export module Controllers { } else { res.end(Buffer.from(response.body), 'binary'); } - return Observable.of(oid); + return of(oid); } catch (error) { if (this.isAjax(req)) { this.ajaxFail(req, res, error.message); @@ -1010,10 +1009,10 @@ export module Controllers { } } } else { - const hasEditAccess = await this.hasEditAccess(brand, req.user, currentRec).toPromise(); + const hasEditAccess = await firstValueFrom(this.hasEditAccess(brand, req.user, currentRec)); if (!hasEditAccess) { sails.log.error("Error: edit error no permissions in do attachment."); - return Observable.throwError(new Error(TranslationService.t('edit-error-no-permissions'))); + return throwError(new Error(TranslationService.t('edit-error-no-permissions'))); } sails.log.verbose(req.headers); let uploadFileSize = req.headers['Upload-Length']; @@ -1026,12 +1025,12 @@ export module Controllers { if (diskSpace.free <= thresholdAppliedFileSize) { let errorMessage = TranslationService.t('not-enough-disk-space'); sails.log.error(errorMessage + ' Total File Size ' + thresholdAppliedFileSize + ' Total Free Space ' + diskSpace.free); - return Observable.throwError(new Error(errorMessage)); + return throwError(new Error(errorMessage)); } } // process the upload... this.tusServer.handle(req, res); - return Observable.of(oid); + return of(oid); } } @@ -1065,7 +1064,7 @@ export module Controllers { public async getPermissionsInternal(req, res) { const oid = req.param('oid'); - let record = await this.getRecord(oid).toPromise(); + let record = await firstValueFrom(this.getRecord(oid)); let response = {}; @@ -1074,7 +1073,7 @@ export module Controllers { if (editUsers != null && editUsers != undefined) { for (let i = 0; i < editUsers.length; i++) { let editUsername = editUsers[i]; - let user = await UsersService.getUserWithUsername(editUsername).toPromise(); + let user = await firstValueFrom(UsersService.getUserWithUsername(editUsername)); editUserResponse.push({ username: editUsername, name: _.get(user, "name", ""), @@ -1087,7 +1086,7 @@ export module Controllers { if (viewUsers != null && viewUsers != undefined) { for (let i = 0; i < viewUsers.length; i++) { let viewUsername = viewUsers[i]; - let user = await UsersService.getUserWithUsername(viewUsername).toPromise(); + let user = await firstValueFrom(UsersService.getUserWithUsername(viewUsername)); viewUserResponse.push({ username: viewUsername, @@ -1122,7 +1121,7 @@ export module Controllers { public getAttachments(req, res) { sails.log.verbose('getting attachments....'); const oid = req.param('oid'); - Observable.fromPromise(this.recordsService.getAttachments(oid)).subscribe((attachments: any[]) => { + from(this.recordsService.getAttachments(oid)).subscribe((attachments: any[]) => { return this.ajaxOk(req, res, null, attachments); }); } @@ -1131,11 +1130,11 @@ export module Controllers { const brand:BrandingModel = BrandingService.getBrand(req.session.branding); const oid = req.param('oid'); const datastreamId = req.param('datastreamId'); - const currentRec = await this.getRecord(oid).toPromise(); + const currentRec = await firstValueFrom(this.getRecord(oid)); - const hasViewAccess = await this.hasViewAccess(brand, req.user, currentRec).toPromise(); + const hasViewAccess = await firstValueFrom(this.hasViewAccess(brand, req.user, currentRec)); if (!hasViewAccess) { - return Observable.throwError(new Error(TranslationService.t('edit-error-no-permissions'))); + return throwError(new Error(TranslationService.t('edit-error-no-permissions'))); } else { const fileName = req.param('fileName') ? req.param('fileName') : datastreamId; res.set('Content-Type', 'application/octet-stream'); @@ -1149,7 +1148,7 @@ export module Controllers { } else { res.end(Buffer.from(response.body), 'binary'); } - return Observable.of(oid); + return of(oid); } catch (error) { if (this.isAjax(req)) { this.ajaxFail(req, res, error.message); diff --git a/typescript/api/controllers/ReportController.ts b/typescript/api/controllers/ReportController.ts index 7d2979cc48..808fb51172 100644 --- a/typescript/api/controllers/ReportController.ts +++ b/typescript/api/controllers/ReportController.ts @@ -20,7 +20,8 @@ // declare var module; declare var sails; -import { Observable } from 'rxjs/Rx'; +import { Observable, of, from } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; declare var BrandingService, RolesService, DashboardService, ReportsService; declare var _; /** @@ -68,7 +69,7 @@ export module Controllers { public getResults(req, res) { const brand:BrandingModel = BrandingService.getBrand(req.session.branding); - var response = Observable.fromPromise(ReportsService.getResults(brand, req.param('name'), req, req.param('start'), req.param('rows'))); + var response = from(ReportsService.getResults(brand, req.param('name'), req, req.param('start'), req.param('rows'))); return response.subscribe(responseObject => { if (responseObject) { let response:any = responseObject; diff --git a/typescript/api/controllers/ReportsController.ts b/typescript/api/controllers/ReportsController.ts index 6fdc440722..88d5323077 100644 --- a/typescript/api/controllers/ReportsController.ts +++ b/typescript/api/controllers/ReportsController.ts @@ -20,7 +20,7 @@ // declare var module; declare var sails; -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; declare var BrandingService, RolesService, DashboardService, ReportsService; /** diff --git a/typescript/api/controllers/WorkspaceTypesController.ts b/typescript/api/controllers/WorkspaceTypesController.ts index 45389c3dad..42347819d1 100644 --- a/typescript/api/controllers/WorkspaceTypesController.ts +++ b/typescript/api/controllers/WorkspaceTypesController.ts @@ -19,7 +19,7 @@ declare var module; declare var sails; -import {Observable} from 'rxjs/Rx'; +import { Observable } from 'rxjs'; import skipperGridFs from "skipper-gridfs"; declare var _; diff --git a/typescript/api/controllers/webservice/FormManagementController.ts b/typescript/api/controllers/webservice/FormManagementController.ts index 9f6808daf3..81c48a5643 100644 --- a/typescript/api/controllers/webservice/FormManagementController.ts +++ b/typescript/api/controllers/webservice/FormManagementController.ts @@ -26,6 +26,7 @@ import {Sails, Model} from "sails"; declare var _; import {Controllers as controllers} from '@researchdatabox/redbox-core-types'; +import { firstValueFrom } from 'rxjs'; declare var sails: Sails; declare var FormsService:formsService.Forms; @@ -65,7 +66,7 @@ export module Controllers { if (editable == null) { editable = true; } - let form:FormModel = await FormsService.getFormByName(name, editable).toPromise(); + let form:FormModel = await firstValueFrom(FormsService.getFormByName(name, editable)); return this.apiRespond(req, res, form, 200) } catch (error) { @@ -75,7 +76,7 @@ export module Controllers { public async listForms(req, res) { try { - let forms:FormModel[] = await FormsService.listForms().toPromise(); + let forms:FormModel[] = await firstValueFrom(FormsService.listForms()); let response: ListAPIResponse < any > = new ListAPIResponse(); let summary: ListAPISummary = new ListAPISummary(); summary.numFound = forms.length; diff --git a/typescript/api/controllers/webservice/RecordController.ts b/typescript/api/controllers/webservice/RecordController.ts index 6abbd74dfb..b62741207a 100644 --- a/typescript/api/controllers/webservice/RecordController.ts +++ b/typescript/api/controllers/webservice/RecordController.ts @@ -33,7 +33,8 @@ declare var User; /** * Package that contains all Controllers. */ -import { Observable } from 'rxjs/Rx'; +import { Observable, from, throwError, of, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; import * as path from "path"; import { APIErrorResponse, @@ -447,7 +448,7 @@ export module Controllers { let createPromise = this.RecordsService.create(brand, request, recordTypeModel, user); - var obs = Observable.fromPromise(createPromise); + var obs = from(createPromise); obs.subscribe(response => { if (response.isSuccessful()) { @@ -850,7 +851,7 @@ export module Controllers { return this.apiFailWrapper(req, res, 400, null, null, "Missing brand."); } - let recordType = await RecordTypesService.get(brand, record.metaMetadata.type).toPromise(); + let recordType = await firstValueFrom(RecordTypesService.get(brand, record.metaMetadata.type)); const response = await this.RecordsService.delete(oid, permanentlyDelete, record, recordType, user); if (response.isSuccessful()) { this.apiRespond(req, res, response); @@ -898,8 +899,8 @@ export module Controllers { return this.apiFailWrapper(req, res, 500, null, null, `User has no edit permissions for :${oid}`); } - const recType = await RecordTypesService.get(brand, record.metaMetadata.type).toPromise(); - const nextStep = await WorkflowStepsService.get(recType, targetStepName).toPromise(); + const recType = await firstValueFrom(RecordTypesService.get(brand, record.metaMetadata.type)); + const nextStep = await firstValueFrom(WorkflowStepsService.get(recType, targetStepName)); const response = await this.RecordsService.updateMeta(brand, oid, record, req.user, true, true, nextStep); this.apiRespond(req, res, response); } catch (err) { @@ -1062,7 +1063,7 @@ export module Controllers { } var recordType = req.param('recordType'); - var recordTypeModel:RecordTypeModel = await RecordTypesService.get(brand, recordType).toPromise(); + var recordTypeModel:RecordTypeModel = await firstValueFrom(RecordTypesService.get(brand, recordType)); if (recordTypeModel == null) { return this.apiFailWrapper(req, res, 400,null, null, "Record Type provided is not valid"); @@ -1116,7 +1117,7 @@ export module Controllers { const brand:BrandingModel = BrandingService.getBrand(req.session.branding); var recordType = req.param('recordType'); - var recordTypeModel:RecordTypeModel = await RecordTypesService.get(brand, recordType).toPromise(); + var recordTypeModel:RecordTypeModel = await firstValueFrom(RecordTypesService.get(brand, recordType)); if (recordTypeModel == null) { return this.apiFailWrapper(req, res, 400,null, null, 'Record Type provided is not valid'); @@ -1258,7 +1259,7 @@ export module Controllers { let response = await this.RecordsService.create(brand, request, recordTypeModel, user); if(workflowStage) { - let wfStep = await WorkflowStepsService.get(recordTypeModel, workflowStage).toPromise(); + let wfStep = await firstValueFrom(WorkflowStepsService.get(recordTypeModel, workflowStage)); this.RecordsService.setWorkflowStepRelatedMetadata(request, wfStep); } diff --git a/typescript/api/controllers/webservice/RecordTypeController.ts b/typescript/api/controllers/webservice/RecordTypeController.ts index ab7007ee5e..2d13e3ab26 100644 --- a/typescript/api/controllers/webservice/RecordTypeController.ts +++ b/typescript/api/controllers/webservice/RecordTypeController.ts @@ -31,6 +31,7 @@ declare var _; * Package that contains all Controllers. */ import {APIErrorResponse, BrandingModel, Controllers as controllers, ListAPIResponse, ListAPISummary} from '@researchdatabox/redbox-core-types'; +import { firstValueFrom } from 'rxjs'; declare var RecordTypesService; @@ -68,7 +69,7 @@ export module Controllers { try { let name = req.param('name'); const brand:BrandingModel = BrandingService.getBrand(req.session.branding); - let recordType = await RecordTypesService.get(brand, name).toPromise(); + let recordType = await firstValueFrom(RecordTypesService.get(brand, name)); return this.apiRespond(req, res, recordType, 200) } catch (error) { @@ -79,7 +80,7 @@ export module Controllers { public async listRecordTypes(req, res) { try { const brand:BrandingModel = BrandingService.getBrand(req.session.branding); - let recordTypes = await RecordTypesService.getAll(brand).toPromise(); + let recordTypes: any[] = await firstValueFrom(RecordTypesService.getAll(brand)); let response: ListAPIResponse < any > = new ListAPIResponse(); let summary: ListAPISummary = new ListAPISummary(); summary.numFound = recordTypes.length; diff --git a/typescript/api/controllers/webservice/SearchController.ts b/typescript/api/controllers/webservice/SearchController.ts index cb28b91d65..7f9ede6aa6 100644 --- a/typescript/api/controllers/webservice/SearchController.ts +++ b/typescript/api/controllers/webservice/SearchController.ts @@ -31,6 +31,7 @@ declare var _; * Package that contains all Controllers. */ import {APIErrorResponse, APIObjectActionResponse, BrandingModel, Controllers as controllers, RecordTypeModel, RecordModel, RecordsService, SearchService} from '@researchdatabox/redbox-core-types'; + import { firstValueFrom } from 'rxjs'; export module Controllers { /** @@ -131,7 +132,7 @@ export module Controllers { // If a record type is set, fetch from the configuration what core it's being sent from if(type != null) { - let recordType:RecordTypeModel = await RecordTypesService.get(brand, type).toPromise(); + let recordType:RecordTypeModel = await firstValueFrom(RecordTypesService.get(brand, type)); core = recordType.searchCore; } diff --git a/typescript/api/helpers/readme.md b/typescript/api/helpers/readme.md deleted file mode 100644 index 9d753b9425..0000000000 --- a/typescript/api/helpers/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -The redbox team has decided not to use sails helpers for now. - -Please do not create sails helpers. - -Ref: https://sailsjs.com/docs/concepts/helpers diff --git a/typescript/api/services/AppConfigService.ts b/typescript/api/services/AppConfigService.ts index 73978c266a..2decae21a5 100644 --- a/typescript/api/services/AppConfigService.ts +++ b/typescript/api/services/AppConfigService.ts @@ -17,7 +17,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; import { BrandingModel, Services as services } from '@researchdatabox/redbox-core-types'; import { Sails } from "sails"; import { find } from 'lodash'; diff --git a/typescript/api/services/AsynchsService.ts b/typescript/api/services/AsynchsService.ts index 96281600ad..0ff12063fb 100644 --- a/typescript/api/services/AsynchsService.ts +++ b/typescript/api/services/AsynchsService.ts @@ -17,14 +17,14 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; import {Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; declare var sails: Sails; declare var _; declare var AsynchProgress: Model; -import { default as moment } from 'moment'; +import { DateTime } from 'luxon'; export module Services { /** @@ -43,7 +43,8 @@ export module Services { public start(progressObj) { if (_.isEmpty(progressObj.date_started) || _.isUndefined(progressObj.date_completed)) { - progressObj.date_started = moment().format('YYYY-MM-DDTHH:mm:ss'); + // Using ISO-like local timestamp without timezone + progressObj.date_started = DateTime.local().toFormat("yyyy-LL-dd'T'HH:mm:ss"); } return super.getObservable(AsynchProgress.create(progressObj)); } @@ -54,9 +55,9 @@ export module Services { public finish(progressId, progressObj=null) { if (progressObj) { - progressObj.date_completed = moment().format('YYYY-MM-DD HH:mm:ss'); + progressObj.date_completed = DateTime.local().toFormat('yyyy-LL-dd HH:mm:ss'); } else { - progressObj = {date_completed: moment().format('YYYY-MM-DD HH:mm:ss')}; + progressObj = {date_completed: DateTime.local().toFormat('yyyy-LL-dd HH:mm:ss')}; } progressObj.status = 'finished'; return super.getObservable(AsynchProgress.update({id:progressId}, progressObj)); diff --git a/typescript/api/services/BrandingService.ts b/typescript/api/services/BrandingService.ts index 1e973ad9d9..b8addbba1b 100644 --- a/typescript/api/services/BrandingService.ts +++ b/typescript/api/services/BrandingService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, of, throwError } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; import {Services as services, BrandingModel} from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; @@ -53,16 +54,16 @@ export module Services { public bootstrap = (): Observable => { return super.getObservable(BrandingConfig.findOne(this.dBrand)) - .flatMap(defaultBrand => { + .pipe(flatMap(defaultBrand => { if (_.isEmpty(defaultBrand)) { // create default brand sails.log.verbose("Default brand doesn't exist, creating..."); return super.getObservable(BrandingConfig.create(this.dBrand)) } sails.log.verbose("Default brand already exists..."); - return Observable.of(defaultBrand); + return of(defaultBrand); }) - .flatMap(this.loadAvailableBrands); + ,flatMap(this.loadAvailableBrands)); } public loadAvailableBrands = (defBrand): Observable => { @@ -70,16 +71,16 @@ export module Services { // Find all the BrandingConfig we have and add them to the availableBrandings array. // A policy is configured to reject any branding values not present in this array. return super.getObservable(BrandingConfig.find({}).populate('roles')) - .flatMap(brands => { + .pipe(flatMap(brands => { this.brandings = brands; this.availableBrandings = _.map(this.brandings, 'name'); var defBrandEntry:BrandingModel = this.getDefault(); if (defBrandEntry == null) { sails.log.error("Failed to load default brand!"); - return Observable.throw(new Error("Failed to load default brand!")); + return throwError(new Error("Failed to load default brand!")); } - return Observable.of(defBrandEntry); - }); + return of(defBrandEntry); + })); } public getDefault = (): BrandingModel => { diff --git a/typescript/api/services/CacheService.ts b/typescript/api/services/CacheService.ts index 0e4d23165f..fced0c43ce 100644 --- a/typescript/api/services/CacheService.ts +++ b/typescript/api/services/CacheService.ts @@ -17,11 +17,12 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, of } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; import {Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; import { default as NodeCache } from "node-cache"; -import { default as moment } from 'moment'; +import { DateTime } from 'luxon'; import { readdir, access } from 'node:fs/promises'; declare var sails: Sails; declare var _; @@ -55,29 +56,29 @@ export module Services { } public get(name): Observable { - const cacheGet = Observable.of(this.cache.get(name)); - return cacheGet.flatMap(data => { + const cacheGet = of(this.cache.get(name)); + return cacheGet.pipe(flatMap(data => { if (data) { - return Observable.of(data); + return of(data); } else { sails.log.verbose(`Getting DB cache entry for name: ${name}`); - return super.getObservable(CacheEntry.findOne({name: name})).flatMap(dbData => { + return super.getObservable(CacheEntry.findOne({name: name})).pipe(flatMap(dbData => { if (!_.isEmpty(dbData)) { sails.log.verbose(`Got DB cache entry`); // check if entry is expired... - if (moment().unix() - dbData.ts_added > sails.config.custom_cache.cacheExpiry) { + if (Math.floor(DateTime.local().toSeconds()) - dbData.ts_added > sails.config.custom_cache.cacheExpiry) { sails.log.verbose(`Cache entry for ${name} has expired while on the DB, returning null...`); - return Observable.of(null); + return of(null); } else { this.cache.set(name, dbData.data); - return Observable.of(dbData.data); + return of(dbData.data); } } sails.log.verbose(`No DB cache entry for: ${name}`); - return Observable.of(null); - }); + return of(null); + })); } - }); + })); } @@ -87,18 +88,18 @@ export module Services { sails.log.verbose(`Setting cache for entry: ${name}...`); this.cache.set(name, data, expiry); super.getObservable(CacheEntry.findOne({name: name})) - .flatMap(dbData => { + .pipe(flatMap(dbData => { if (!_.isEmpty(dbData)) { sails.log.verbose(`Updating entry name: ${name}`) - return super.getObservable(CacheEntry.update({name: name}, {name: name, data:data, ts_added: moment().unix()})); + return super.getObservable(CacheEntry.update({name: name}, {name: name, data:data, ts_added: Math.floor(DateTime.local().toSeconds())})); } else { sails.log.verbose(`Creating entry name: ${name}`); - return super.getObservable(CacheEntry.create({name: name, data:data, ts_added: moment().unix()})); + return super.getObservable(CacheEntry.create({name: name, data:data, ts_added: Math.floor(DateTime.local().toSeconds())})); } }) - .flatMap(dbData => { - return Observable.of(dbData); - }) + ,flatMap(dbData => { + return of(dbData); + })) .subscribe(data => { sails.log.verbose(`Saved local and remote cache for entry:${name}`); }, error => { diff --git a/typescript/api/services/ConfigService.ts b/typescript/api/services/ConfigService.ts index 458bf53892..235857997a 100644 --- a/typescript/api/services/ConfigService.ts +++ b/typescript/api/services/ConfigService.ts @@ -17,7 +17,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; import {BrandingModel, Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; import * as fs from 'fs-extra'; diff --git a/typescript/api/services/DashboardTypesService.ts b/typescript/api/services/DashboardTypesService.ts index 0a46c057e1..b45b70bfdb 100644 --- a/typescript/api/services/DashboardTypesService.ts +++ b/typescript/api/services/DashboardTypesService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, zip, of, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; import {Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; @@ -58,7 +59,7 @@ export module Services { for(let dashboardType in sails.config.dashboardtype) { dashboardTypes.push(dashboardType); let config = sails.config.dashboardtype[dashboardType]; - var createdDashboardType = await this.create(defBrand, dashboardType, config).toPromise(); + var createdDashboardType = await firstValueFrom(this.create(defBrand, dashboardType, config)); dashTypes.push(createdDashboardType); }; this.dashboardTypes = dashboardTypes; diff --git a/typescript/api/services/DoiService.ts b/typescript/api/services/DoiService.ts index 8300a22e81..4c593ef7db 100644 --- a/typescript/api/services/DoiService.ts +++ b/typescript/api/services/DoiService.ts @@ -18,8 +18,8 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import { - Observable -} from 'rxjs/Rx'; + of +} from 'rxjs'; import { Services as services, RBValidationError, @@ -29,8 +29,8 @@ import { Sails, Model } from "sails"; -import 'rxjs/add/operator/toPromise'; -import {default as moment} from 'moment'; +import { DateTime } from 'luxon'; +import moment from '../shims/momentShim'; import axios from 'axios'; import { isArray } from 'lodash'; @@ -450,8 +450,8 @@ export module Services { if (!_.isEmpty(postBody.data.attributes.dates)) { let dates = postBody.data.attributes.dates for (var i = 0; i < _.size(dates); i++) { - let date = moment(new Date(dates[i].date)).format('YYYY-MM-DD') - if (!moment(date, 'YYYY-MM-DD', true).isValid()) { + let date = DateTime.fromJSDate(new Date(dates[i].date)).toFormat('yyyy-LL-dd') + if (!DateTime.fromFormat(date, 'yyyy-LL-dd', { zone: 'utc' }).isValid) { postBodyValidateError.push('date-invalid') } } @@ -513,7 +513,7 @@ export module Services { } } - return Observable.of(null); + return of(null); } public async publishDoiTriggerSync(oid, record, options): Promise { diff --git a/typescript/api/services/EmailService.ts b/typescript/api/services/EmailService.ts index b8ad8719a8..a0101f7441 100644 --- a/typescript/api/services/EmailService.ts +++ b/typescript/api/services/EmailService.ts @@ -18,14 +18,14 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import { - Observable -} from 'rxjs/Rx'; + Observable, from, of, throwError, firstValueFrom +} from 'rxjs'; import { Services as services } from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; -import 'rxjs/add/operator/toPromise'; +// removed deprecated rxjs/add/operator imports; use firstValueFrom instead import * as ejs from 'ejs'; import * as fs from 'graceful-fs'; import * as nodemailer from 'nodemailer'; @@ -82,7 +82,7 @@ export module Services { otherSendOptions: { [dict_key: string]: any } = _.get(sails.config.emailnotification.defaults, 'otherSendOptions', {}), ): Observable<{ success: boolean, msg: string }> { - return Observable.fromPromise(this.sendMessageAsync(msgTo, msgBody, msgSubject, msgFrom, msgFormat, cc, bcc, otherSendOptions)); + return from(this.sendMessageAsync(msgTo, msgBody, msgSubject, msgFrom, msgFormat, cc, bcc, otherSendOptions)); } @@ -205,7 +205,7 @@ export module Services { * @return A promise that evaluates to the response object with 'status', 'body', and maybe 'ex' set. */ public buildFromTemplate(template: string, data: any = {}): Observable { - return Observable.fromPromise(this.buildFromTemplateAsync(template, data)); + return from(this.buildFromTemplateAsync(template, data)); } /** @@ -288,13 +288,13 @@ export module Services { throw new Error('Invalid email address.'); } - const buildResult = await optionsEvaluated.templateRendered.toPromise(); + const buildResult = await firstValueFrom(optionsEvaluated.templateRendered); if (buildResult['status'] != 200) { sails.log.error(`Failed to build email body ${msgPartial}, result: ${JSON.stringify(buildResult)}`); throw new Error('Invalid email body.'); } - const sendResult = await this.sendMessage( + const sendResult = await firstValueFrom(this.sendMessage( optionsEvaluated.toRendered, buildResult['body'], optionsEvaluated.subjectRendered, @@ -303,7 +303,7 @@ export module Services { optionsEvaluated.ccRendered, optionsEvaluated.bccRendered, _.get(options, 'otherSendOptions', {}), - ).toPromise(); + )); if (sendResult.success) { sails.log.verbose(`Record send notification succeeded ${msgPartial}`); @@ -319,12 +319,12 @@ export module Services { let postSendHookResult = postSendHookFn(oid, record, postSendHookOpts, user, response); if (isObservable(postSendHookResult)) { - postSendHookResult = postSendHookResult.toPromise(); + postSendHookResult = firstValueFrom(postSendHookResult); } else { postSendHookResult = Promise.resolve(postSendHookResult); } - postSendHookResult.then(result => { + (postSendHookResult as Promise).then(result => { sails.log.verbose(`Post notification ${msgPartial} sending hook '${postSendHookFnName}' completed with result: ${JSON.stringify(result)}`); }).catch(error => { sails.log.verbose(`Post notification ${msgPartial} sending hook '${postSendHookFnName}' failed with error: ${JSON.stringify(error)}`); diff --git a/typescript/api/services/FigshareService.ts b/typescript/api/services/FigshareService.ts index 7375927988..87096a15b6 100644 --- a/typescript/api/services/FigshareService.ts +++ b/typescript/api/services/FigshareService.ts @@ -19,7 +19,7 @@ import { Services as services, DatastreamService, RBValidationError, QueueService, BrandingModel, FigshareArticleCreate, FigshareArticleUpdate, FigshareArticleEmbargo } from '@researchdatabox/redbox-core-types'; import { Sails } from "sails"; -const moment = require('moment'); +import moment from '../shims/momentShim'; const axios = require('axios'); const _ = require('lodash'); const fs = require('fs'); diff --git a/typescript/api/services/FormsService.ts b/typescript/api/services/FormsService.ts index 533fa79a37..576acc9c47 100644 --- a/typescript/api/services/FormsService.ts +++ b/typescript/api/services/FormsService.ts @@ -17,9 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { - Observable -} from 'rxjs/Rx'; +import { Observable, of, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap, last, filter } from 'rxjs/operators'; import { BrandingModel, FormModel, Services as services } from '@researchdatabox/redbox-core-types'; import { Sails, @@ -153,13 +152,13 @@ export module Services { public getFormByName = (formName, editMode): Observable => { return super.getObservable(Form.findOne({ name: formName - })).flatMap(form => { + })).pipe(flatMap(form => { if (form) { this.setFormEditMode(form.fields, editMode); - return Observable.of(form); + return of(form); } - return Observable.of(null); - }); + return of(null); + })); } public async getForm(branding: BrandingModel, formParam: string, editMode: boolean, recordType: string, currentRec: any) { @@ -171,7 +170,7 @@ export module Services { return await this.generateFormFromSchema(branding, recordType, currentRec); } else { - return await this.getFormByName(formName, editMode).toPromise(); + return await firstValueFrom(this.getFormByName(formName, editMode)); } } @@ -181,31 +180,31 @@ export module Services { return super.getObservable(RecordType.findOne({ key: branding.id + "_" + recordType - })) - .flatMap(recordType => { - + })).pipe( + flatMap(recordType => { return super.getObservable(WorkflowStep.findOne({ recordType: recordType.id, starting: starting })); - }).flatMap(workflowStep => { - + }), + flatMap(workflowStep => { if (workflowStep.starting == true) { - return super.getObservable(Form.findOne({ name: workflowStep.config.form })); } - - return Observable.of(null); - }).flatMap(form => { - + return of(null); + }), + flatMap(form => { if (form) { this.setFormEditMode(form.fields, editMode); - return Observable.of(form); + return of(form); } - return Observable.of(null); - }).filter(result => result !== null).last(); + return of(null); + }), + filter(result => result !== null), + last() + ); } public inferSchemaFromMetadata(record: any): any { diff --git a/typescript/api/services/NamedQueryService.ts b/typescript/api/services/NamedQueryService.ts index 9a1f46c910..7a10ec5040 100644 --- a/typescript/api/services/NamedQueryService.ts +++ b/typescript/api/services/NamedQueryService.ts @@ -28,12 +28,12 @@ declare var sails: Sails; declare var Record: Model; declare var User: Model; declare var NamedQuery: Model; -const moment = require('moment'); +import { DateTime } from 'luxon'; declare var _this; declare var _; import { ListAPIResponse } from '@researchdatabox/redbox-core-types'; -import { Observable } from 'rxjs'; +import { Observable, firstValueFrom } from 'rxjs'; export module Services { /** @@ -52,10 +52,10 @@ export module Services { "performNamedQueryFromConfigResults", ]; - public async bootstrap (defBrand) { - let namedQueries = await super.getObservable(NamedQuery.find({ + public async bootstrap (defBrand) { + const namedQueries = await firstValueFrom(super.getObservable(NamedQuery.find({ branding: defBrand.id - })).toPromise() + }))); if (!_.isEmpty(namedQueries)) { if (sails.config.appmode.bootstrapAlways) { @@ -67,13 +67,13 @@ export module Services { } } sails.log.verbose("Bootstrapping named query definitions... "); - await this.createNamedQueriesForBrand(defBrand); + await this.createNamedQueriesForBrand(defBrand); } private async createNamedQueriesForBrand(defBrand: any) { for (const [namedQuery, config] of Object.entries(sails.config.namedQuery)) { const namedQueryConfig: any = config; - await this.create(defBrand, namedQuery, namedQueryConfig).toPromise(); + await firstValueFrom(this.create(defBrand, namedQuery, namedQueryConfig)); } } @@ -261,17 +261,17 @@ export module Services { } if(queryParam.format == NamedQueryFormatOptions.days) { let days = _.toInteger(value); - let nowDateAddOrSubtract = moment(); + let nowDateAddOrSubtract = DateTime.local(); if (days > 0) { //Going forward in time X number of days - nowDateAddOrSubtract = nowDateAddOrSubtract.add(days, 'days'); + nowDateAddOrSubtract = nowDateAddOrSubtract.plus({ days: days }); } else if(days < 0) { //This "additional" step makes the code self explanatory days = days * -1; //Going backwards in time X number of days - nowDateAddOrSubtract = nowDateAddOrSubtract.subtract(days, 'days'); + nowDateAddOrSubtract = nowDateAddOrSubtract.minus({ days: days }); } - value = nowDateAddOrSubtract.toISOString(); + value = nowDateAddOrSubtract.toISO(); } query[queryParam.queryType] = value; diff --git a/typescript/api/services/OniService.ts b/typescript/api/services/OniService.ts index 7facfcfb81..e39dd886ce 100644 --- a/typescript/api/services/OniService.ts +++ b/typescript/api/services/OniService.ts @@ -19,7 +19,7 @@ import { Services as services, DatastreamService, RBValidationError } from '@researchdatabox/redbox-core-types'; import { Sails } from "sails"; -import 'rxjs/add/operator/toPromise'; +import { firstValueFrom } from 'rxjs'; import { promises as fs } from 'fs'; import path from 'node:path'; import {Collector, generateArcpId} from "oni-ocfl"; @@ -150,7 +150,7 @@ export module Services { throw this.getRBError(`${this.logHeader} exportDataset()`, `Error loading root collection ${site.rootCollectionId}: ${err}`); } - let creator = await UsersService.getUserWithUsername(record['metaMetadata']['createdBy']).toPromise(); + let creator = await firstValueFrom(UsersService.getUserWithUsername(record['metaMetadata']['createdBy'])); if (_.isEmpty(creator)) { throw this.getRBError(`${this.logHeader} exportDataset()`, `Error getting creator for record ${oid} :: ${record['metaMetadata']['createdBy']}`); diff --git a/typescript/api/services/OrcidService.ts b/typescript/api/services/OrcidService.ts index ab7aada3b2..0c05e35c0f 100644 --- a/typescript/api/services/OrcidService.ts +++ b/typescript/api/services/OrcidService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { of, from } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; import {Services as services} from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; // import * as request from "request-promise"; @@ -52,9 +53,9 @@ export module Services { var url = sails.config.orcid.url + '/v1.2/search/orcid-bio/?q=family-name:"' + familyName + '"%20AND%20given-names:"' + givenNames + '"&start=' + start + '&rows=' + rows; var options = this.getOptions(url, sails.config.record.api.search.method); - var orcidRes = Observable.fromPromise(axios(options)); + var orcidRes = from(axios(options)); - return orcidRes.flatMap(orcidResult => { + return orcidRes.pipe(flatMap(orcidResult => { var results = {}; results["numFound"] = orcidResult["orcid-search-results"]["num-found"]; results["items"] = []; @@ -64,8 +65,8 @@ export module Services { var item = this.mapToPeopleSearchResult(orcidSearchResult); results["items"].push(item); } - return Observable.of(results); - }); + return of(results); + })); } protected mapToPeopleSearchResult(orcidSearchResult) { diff --git a/typescript/api/services/PathRulesService.ts b/typescript/api/services/PathRulesService.ts index b009869fc8..74e192cf75 100644 --- a/typescript/api/services/PathRulesService.ts +++ b/typescript/api/services/PathRulesService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, from, of } from 'rxjs'; +import { mergeMap as flatMap, last } from 'rxjs/operators'; import {BrandingModel, Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; import { default as UrlPattern } from 'url-pattern'; @@ -52,7 +53,7 @@ export module Services { sails.log.verbose("Bootstrapping path rules...."); var defBrand:BrandingModel = BrandingService.getDefault(); return this.loadRules() - .flatMap(rules => { + .pipe(flatMap(rules => { if (!rules || rules.length == 0) { sails.log.verbose("Rules, don't exist, seeding..."); var seedRules = sails.config.auth.rules; @@ -61,22 +62,22 @@ export module Services { rule.role = role.id; rule.branding = defBrand.id; }); - return Observable.from(seedRules) - .flatMap(rule => { + return from(seedRules) + .pipe(flatMap(rule => { return super.getObservable(PathRule.create(rule)); }) - .last() - .flatMap(rule => { + ,last() + ,flatMap(rule => { return this.loadRules(); }) - .flatMap(rules => { - return Observable.of(rules); - }); + ,flatMap(rules => { + return of(rules); + })); } else { sails.log.verbose("Rules exists."); - return Observable.of(rules); + return of(rules); } - }); + })); } /** @@ -84,14 +85,14 @@ export module Services { */ public loadRules = () => { return super.getObservable(PathRule.find({}).populate('role').populate('branding')) - .flatMap(rules => { + .pipe(flatMap(rules => { this.pathRules = rules; this.rulePatterns = []; _.forEach(rules, (rule) => { this.rulePatterns.push({pattern: new UrlPattern(rule.path), rule: rule}); }); - return Observable.of(this.pathRules); - }); + return of(this.pathRules); + })); } /** * Check path using cached rules... diff --git a/typescript/api/services/RDMPService.ts b/typescript/api/services/RDMPService.ts index 84703fdc9c..c3e64ae19f 100644 --- a/typescript/api/services/RDMPService.ts +++ b/typescript/api/services/RDMPService.ts @@ -17,9 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { - Observable -} from 'rxjs/Rx'; +import { Observable, of, from, zip, throwError, isObservable, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; import { QueueService, Services as services, @@ -30,12 +29,10 @@ import { Sails, Model } from "sails"; -import { default as moment } from 'moment'; +import moment from '../shims/momentShim'; import numeral from 'numeral'; -import { - isObservable -} from 'rxjs'; +// removed duplicate isObservable import declare var sails: Sails; declare var RecordType, Counter: Model; @@ -129,14 +126,14 @@ export module Services { sails.log[processRecordCountersLogLevel]('processRecordCounters - before - counter:'); sails.log[processRecordCountersLogLevel](counter); - const promiseCounter = await this.getObservable(Counter.findOrCreate({ + const promiseCounter = await firstValueFrom(this.getObservable(Counter.findOrCreate({ name: counter.field_name, branding: brandId }, { name: counter.field_name, branding: brandId, value: 0 - })).toPromise(); + }))); if (_.isEmpty(promiseCounter)) { sails.log[processRecordCountersLogLevel]('processRecordCounters - promiseCounter isEmpty'); @@ -155,11 +152,11 @@ export module Services { this.incrementCounter(record, counter, newVal); //Update global counter - const updateOnePromise = await this.getObservable(Counter.updateOne({ + const updateOnePromise = await firstValueFrom(this.getObservable(Counter.updateOne({ id: promiseCounter[0].id }, { value: newVal - })).toPromise(); + }))); sails.log[processRecordCountersLogLevel]('processRecordCounters - updateOnePromise:'); sails.log[processRecordCountersLogLevel](updateOnePromise); } @@ -396,7 +393,7 @@ export module Services { sails.log.verbose(queueMessage); this.queueService.now(jobName, queueMessage); } - return Observable.of(record); + return of(record); } public queuedTriggerSubscriptionHandler(job: any) { @@ -416,7 +413,7 @@ export module Services { sails.log.debug(`Triggering queuedtrigger: ${hookFunctionString}`) let hookResponse = hookFunction(oid, record, options, user); let response = this.convertToObservable(hookResponse); - return response.toPromise(); + return firstValueFrom(response); } else { sails.log.error(`queued trigger function: '${hookFunctionString}' did not resolve to a valid function, what I got:`); @@ -441,7 +438,7 @@ export module Services { sails.log.debug(`Triggering queuedtrigger: ${figshareUploadHookFunctionString}`) let hookResponse = figshareUploadHookFunction(oid, attachId, articleId, dataPublicationRecord, fileName, fileSize); let response = this.convertToObservable(hookResponse); - return response.toPromise(); + return firstValueFrom(response); } else { sails.log.error(`queued trigger function: '${figshareUploadHookFunctionString}' did not resolve to a valid function, what I got:`); @@ -455,7 +452,7 @@ export module Services { if (isObservable(hookResponse)) { return hookResponse; } else { - response = Observable.fromPromise(hookResponse); + response = from(hookResponse); } return response; } @@ -496,7 +493,7 @@ export module Services { viewContributorEmails, viewContributorObs ); } - return Observable.of(record); + return of(record); } /** @@ -532,8 +529,8 @@ export module Services { editContributorEmails, editContributorObs, viewContributorEmails, viewContributorObs ); - } - return Observable.of(record); + } + return of(record); } /** @@ -559,7 +556,7 @@ export module Services { } // when both are empty, simpy return the record if (_.isEmpty(editContributorEmails) && _.isEmpty(viewContributorEmails)) { - return Observable.of(record); + return of(record); } _.each(editContributorEmails, editorEmail => { editContributorObs.push(this.getObservable(User.findOne({ @@ -573,10 +570,10 @@ export module Services { }); let zippedViewContributorUsers; if (editContributorObs.length == 0) { - zippedViewContributorUsers = Observable.zip(...viewContributorObs); + zippedViewContributorUsers = zip(...viewContributorObs); } else { - zippedViewContributorUsers = Observable.zip(...editContributorObs) - .flatMap(editContributorUsers => { + zippedViewContributorUsers = zip(...editContributorObs) + .pipe(flatMap(editContributorUsers => { let newEditList = []; this.filterPending(editContributorUsers, editContributorEmails, newEditList); if (recordCreatorPermissions == "edit" || recordCreatorPermissions == "view&edit") { @@ -585,16 +582,16 @@ export module Services { record.authorization.edit = newEditList; record.authorization.editPending = editContributorEmails; if (viewContributorObs.length === 0) { - return Observable.of(record); + return of(record); } else { - return Observable.zip(...viewContributorObs); + return zip(...viewContributorObs); } - }); + })); } if (zippedViewContributorUsers.length == 0) { return zippedViewContributorUsers; } else { - return zippedViewContributorUsers.flatMap(viewContributorUsers => { + return zippedViewContributorUsers.pipe(flatMap(viewContributorUsers => { let newViewList = []; this.filterPending(viewContributorUsers, viewContributorEmails, newViewList); if (recordCreatorPermissions == "view" || recordCreatorPermissions == "view&edit") { @@ -602,8 +599,8 @@ export module Services { } record.authorization.view = newViewList; record.authorization.viewPending = viewContributorEmails; - return Observable.of(record); - }); + return of(record); + })); } } @@ -646,7 +643,7 @@ export module Services { } } } - return Observable.of(record); + return of(record); } public restoreUserBasedPermissions(oid, record, options, user) { @@ -663,7 +660,7 @@ export module Services { delete record.authorization.stored } } - return Observable.of(record); + return of(record); } public runTemplates(oid, record, options, user, response: StorageServiceResponse = null) { @@ -708,10 +705,10 @@ export module Services { const errLog = `Failed to run one of the string templates: ${JSON.stringify(tmplConfig)}` sails.log.error(errLog); sails.log.error(e); - return Observable.throw(new Error(errLog)); + return throwError(new Error(errLog)); } - return Observable.of(record); + return of(record); } diff --git a/typescript/api/services/RaidService.ts b/typescript/api/services/RaidService.ts index 0652123020..4d5530fce3 100644 --- a/typescript/api/services/RaidService.ts +++ b/typescript/api/services/RaidService.ts @@ -27,7 +27,7 @@ import { } from "sails"; import { RaidApi, RaidCreateRequest, Title, ModelDate, Description, Access, AlternateUrl, Contributor, ContributorRoleCreditNisoOrgType, ContributorRoleSchemeType, Organisation } from '@researchdatabox/raido-openapi-generated-node'; -import moment = require('moment'); +import moment from '../shims/momentShim'; import numeral from 'numeral'; import axios from 'axios'; diff --git a/typescript/api/services/RecordTypesService.ts b/typescript/api/services/RecordTypesService.ts index 614993777a..1eff17caf3 100644 --- a/typescript/api/services/RecordTypesService.ts +++ b/typescript/api/services/RecordTypesService.ts @@ -17,7 +17,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, firstValueFrom } from 'rxjs'; import {BrandingModel, RecordTypeModel, Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; @@ -68,7 +68,7 @@ export module Services { let rTypes = []; for(let recordType in sails.config.recordtype) { let config:RecordTypeModel = sails.config.recordtype[recordType]; - rTypes.push(await this.create(defBrand, recordType, config).toPromise()) + rTypes.push(await firstValueFrom(this.create(defBrand, recordType, config))) } return rTypes; } diff --git a/typescript/api/services/RecordsService.ts b/typescript/api/services/RecordsService.ts index c67c7a9390..b5d437368b 100644 --- a/typescript/api/services/RecordsService.ts +++ b/typescript/api/services/RecordsService.ts @@ -18,8 +18,9 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import { - Observable -} from 'rxjs/Rx'; + Observable, of, from, mergeMap as flatMap, firstValueFrom, throwError +} from 'rxjs'; +import { concatMap, last, catchError } from 'rxjs/operators'; import { DatastreamService, @@ -41,7 +42,7 @@ import { import axios from 'axios'; import * as luceneEscapeQuery from "lucene-escape-query"; import * as fs from 'fs'; -import { default as moment } from 'moment'; +import { DateTime } from 'luxon'; import { isObservable @@ -185,18 +186,18 @@ export module Services { - let wfStep = await WorkflowStepsService.getFirst(recordType).toPromise(); + let wfStep = await firstValueFrom(WorkflowStepsService.getFirst(recordType)); let formName = _.get(wfStep, 'config.form'); - let form = await FormsService.getForm(brand, formName, true, recordType.name, record); + let form = await FormsService.getForm(brand, formName, true, recordType.name, record); - let metaMetadata = this.initRecordMetaMetadata(brand.id, user.username, recordType, wfStep, form, moment().format()); + let metaMetadata = this.initRecordMetaMetadata(brand.id, user.username, recordType, wfStep, form, DateTime.local().toISO()); _.set(record,'metaMetadata',metaMetadata); //set the initial workflow metadata to the first step this.setWorkflowStepRelatedMetadata(record, wfStep); if (targetStep) { - wfStep = await WorkflowStepsService.get(recordType.name, targetStep).toPromise(); + wfStep = await firstValueFrom(WorkflowStepsService.get(recordType.name, targetStep)); record = await this.triggerPreSaveTransitionWorkflowTriggers(null, record, recordType, wfStep, user); this.setWorkflowStepRelatedMetadata(record, wfStep); } @@ -254,7 +255,7 @@ export module Services { }); // update the datastreams in RB, this is a terminal call sails.log.verbose(`RecordsService - create - before handleUpdateDataStream`); - let resposeDatastream = await this.handleUpdateDataStream(oid, emptyDatastreamRecord, record.metadata).toPromise(); + let resposeDatastream = await firstValueFrom(this.handleUpdateDataStream(oid, emptyDatastreamRecord, record.metadata)); } catch (error) { throw new Error(`RecordsService - create - Failed to save record: ${error}`); } @@ -348,7 +349,7 @@ export module Services { let recordType = null; if (!_.isEmpty(brand)) { - recordType = await RecordTypesService.get(brand, record.metaMetadata.type).toPromise(); + recordType = await firstValueFrom(RecordTypesService.get(brand, record.metaMetadata.type)); } if (!_.isEmpty(nextStep) && !_.isEmpty(nextStep.config)) { @@ -385,14 +386,14 @@ export module Services { } } - let form = await FormsService.getFormByName(record.metaMetadata.form, true).toPromise() + let form: any = await firstValueFrom(FormsService.getFormByName(record.metaMetadata.form, true)) record.metaMetadata.attachmentFields = form != undefined ? form.attachmentFields : []; // process pre-save if (!_.isEmpty(brand) && triggerPreSaveTriggers === true) { try { sails.log.verbose('RecordService - updateMeta - calling triggerPreSaveTriggers'); - recordType = await RecordTypesService.get(brand, record.metaMetadata.type).toPromise(); + recordType = await firstValueFrom(RecordTypesService.get(brand, record.metaMetadata.type)); record = await this.triggerPreSaveTriggers(oid, record, recordType, 'onUpdate', user); } catch (err) { sails.log.error(`${this.logHeader} Failed to run pre-save hooks when onUpdate...`); @@ -409,7 +410,7 @@ export module Services { sails.log.verbose(`RecordService - updateMeta - origRecord.metadata.dataLocations ` + JSON.stringify(origRecord.metadata.dataLocations)); sails.log.verbose(`RecordService - updateMeta - record.metadata.dataLocations ` + JSON.stringify(record.metadata.dataLocations)); - updateResponse = await this.handleUpdateDataStream(oid, origRecord, record.metadata).toPromise(); + updateResponse = await firstValueFrom(this.handleUpdateDataStream(oid, origRecord, record.metadata)); sails.log.verbose(`RecordService - updateMeta - Done with updating streams...`); const fieldsToCheck = ['location', 'uploadUrl']; @@ -438,7 +439,7 @@ export module Services { if(!_.isUndefined(user) && !_.isEmpty(_.get(user,'username',''))) { record.metaMetadata.lastSavedBy = _.get(user,'username'); } - record.metaMetadata.lastSaveDate = moment().format(); + record.metaMetadata.lastSaveDate = DateTime.local().toISO(); // update updateResponse = await this.storageService.updateMeta(brand, oid, record, user); sails.log.verbose('RecordService - updateMeta - updateResponse.isSuccessful ' + updateResponse.isSuccessful()); @@ -446,7 +447,7 @@ export module Services { //if triggerPreSaveTriggers is false recordType will be empty even if triggerPostSaveTriggers is true //therefore try to set recordType if triggerPostSaveTriggers is true if (_.isEmpty(recordType) && !_.isEmpty(brand) && triggerPostSaveTriggers === true) { - recordType = await RecordTypesService.get(brand, record.metaMetadata.type).toPromise(); + recordType = await firstValueFrom(RecordTypesService.get(brand, record.metaMetadata.type)); } // post-save async if (!_.isEmpty(recordType) && triggerPostSaveTriggers === true) { @@ -617,7 +618,7 @@ export module Services { let attachments = []; _.each(datastreams, (datastream) => { let attachment = {}; - attachment['dateUpdated'] = moment(datastream['uploadDate']).format(); + attachment['dateUpdated'] = DateTime.fromJSDate(new Date(datastream['uploadDate'])).toISO(); attachment['label'] = _.get(datastream.metadata, 'name'); attachment['contentType'] = _.get(datastream.metadata, 'mimeType'); attachment = _.merge(attachment, datastream.metadata); @@ -877,8 +878,8 @@ export module Services { sails.log.debug(`Searching fuzzy using: ${url}`); const options = this.getOptions(url, sails.config.record.api.search.method); - return Observable.fromPromise(axios(options)) - .flatMap(resp => { + return firstValueFrom(from(axios(options)) + .pipe(flatMap(resp => { let response: any = resp; const customResp = { records: [] @@ -913,8 +914,8 @@ export module Services { }); }); } - return Observable.of(customResp); - }).toPromise(); + return of(customResp); + }))); } protected addAuthFilter(url, username, roles, brand, editAccessOnly = undefined) { @@ -1217,7 +1218,7 @@ export module Services { private resolveHookResponse(hookResponse) { let response = hookResponse; if (isObservable(hookResponse)) { - response = hookResponse.toPromise(); + response = firstValueFrom(hookResponse); } else { response = Promise.resolve(hookResponse); } @@ -1236,38 +1237,42 @@ export module Services { public handleUpdateDataStream(oid, origRecord, metadata) { const fileIdsAdded = []; - - return this.datastreamService.updateDatastream(oid, origRecord, metadata, sails.config.record.attachments.stageDir, fileIdsAdded) - .concatMap(reqs => { - if (reqs) { - sails.log.verbose(`Updating data streams...`); - return Observable.from(reqs); - } else { - sails.log.verbose(`No datastreams to update...`); - return Observable.of(null); - } - }) - .concatMap((promise) => { - if (promise) { - sails.log.verbose(`Update datastream request is...`); - sails.log.verbose(JSON.stringify(promise)); - return promise.catch(e => { - sails.log.verbose(`Error in updating stream::::`); - sails.log.verbose(JSON.stringify(e)); - return Observable.throwError(new Error(TranslationService.t('attachment-upload-error'))); - }); - } else { - return Observable.of(null); - } - }) - .concatMap(updateResp => { - if (updateResp) { - sails.log.verbose(`Got response from update datastream request...`); - sails.log.verbose(JSON.stringify(updateResp)); - } - return Observable.of(updateResp); - }) - .last(); + return this.datastreamService + .updateDatastream(oid, origRecord, metadata, sails.config.record.attachments.stageDir, fileIdsAdded) + .pipe( + concatMap((reqs: any) => { + if (reqs) { + sails.log.verbose(`Updating data streams...`); + return from(reqs as any[]); + } else { + sails.log.verbose(`No datastreams to update...`); + return of(null); + } + }), + concatMap((promise: any) => { + if (promise) { + sails.log.verbose(`Update datastream request is...`); + sails.log.verbose(JSON.stringify(promise)); + return from(promise).pipe( + catchError((e: any) => { + sails.log.verbose(`Error in updating stream::::`); + sails.log.verbose(JSON.stringify(e)); + return throwError(new Error(TranslationService.t('attachment-upload-error'))); + }) + ); + } else { + return of(null); + } + }), + concatMap(updateResp => { + if (updateResp) { + sails.log.verbose(`Got response from update datastream request...`); + sails.log.verbose(JSON.stringify(updateResp)); + } + return of(updateResp); + }), + last() + ); } } } diff --git a/typescript/api/services/ReportsService.ts b/typescript/api/services/ReportsService.ts index 662c6bee0a..62f354d275 100644 --- a/typescript/api/services/ReportsService.ts +++ b/typescript/api/services/ReportsService.ts @@ -17,9 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { - Observable -} from 'rxjs/Rx'; +import { Observable, from, of, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap, last } from 'rxjs/operators'; import { ListAPIResponse, ReportConfig, ReportModel, ReportFilterType, ReportSource, ReportResult, SearchService, Services as services } from '@researchdatabox/redbox-core-types'; import { DateTime } from 'luxon'; import { @@ -64,7 +63,7 @@ export module Services { public bootstrap = (defBrand) => { return super.getObservable(Report.find({ branding: defBrand.id - })).flatMap(reports => { + })).pipe(flatMap(reports => { if (_.isEmpty(reports)) { var rTypes = []; sails.log.verbose("Bootstrapping report definitions... "); @@ -73,19 +72,18 @@ export module Services { obs.subscribe(repProcessed => { }) rTypes.push(obs); }); - return Observable.from(rTypes); + return from(rTypes); } else { var rTypes = []; _.each(reports, function (report) { - rTypes.push(Observable.of(report)); + rTypes.push(of(report)); }); sails.log.verbose("Default reports definition(s) exist."); - return Observable.from(rTypes); + return from(rTypes); } - }) - .last(); + }),last()); } public findAllReportsForBrand(brand) { @@ -152,7 +150,7 @@ export module Services { key: brand.id + "_" + name })); - let reportObject = await reportObs.toPromise() + let reportObject = await firstValueFrom(reportObs) reportObject = this.convertLegacyReport(reportObject); @@ -263,9 +261,9 @@ export module Services { public async getCSVResult(brand, name = '', req, start = 0, rows = 1000000000) { - var report:ReportModel = await super.getObservable(Report.findOne({ + var report:ReportModel = await firstValueFrom(super.getObservable(Report.findOne({ key: brand.id + "_" + name - })).toPromise(); + }))); report = this.convertLegacyReport(report); diff --git a/typescript/api/services/RolesService.ts b/typescript/api/services/RolesService.ts index 7790dd18b9..1812b1f98a 100644 --- a/typescript/api/services/RolesService.ts +++ b/typescript/api/services/RolesService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, of, from, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap, last, first } from 'rxjs/operators'; import {BrandingModel, Services as services} from '@researchdatabox/redbox-core-types'; import {Sails, Model} from "sails"; @@ -52,27 +53,27 @@ export module Services { 'createRoleWithBrand' ]; - public getRoleWithName = (roles, roleName) :any => { - return _.find(roles, (o) => {return o.name == roleName}); + public getRoleWithName = (roles, roleName): any => { + return _.find(roles, (o) => { return o.name == roleName }); } - public getRole = (brand, roleName) :any => { + public getRole = (brand, roleName): any => { return this.getRoleWithName(brand.roles, roleName); } - public getRoleByName = (brand, roleName) :any => { + public getRoleByName = (brand, roleName): any => { return this.getRoleWithName(brand.roles, this.getConfigRole(roleName).name); } - public getAdmin = (brand) :any => { + public getAdmin = (brand): any => { return this.getRole(brand, this.getConfigRole('Admin').name); } - public getAdminFromRoles = (roles) :any => { + public getAdminFromRoles = (roles): any => { return this.getRoleWithName(roles, this.getConfigRole('Admin').name); } - public getDefAuthenticatedRole = (brand:any) :any => { + public getDefAuthenticatedRole = (brand: any): any => { sails.log.verbose(this.getRoleWithName(brand.roles, this.getConfigRole(ConfigService.getBrand(brand.name, 'auth').aaf.defaultRole).name)); return this.getRoleWithName(brand.roles, this.getConfigRole(ConfigService.getBrand(brand.name, 'auth').aaf.defaultRole).name); } @@ -93,48 +94,48 @@ export module Services { return roles; } - public getDefUnathenticatedRole = (brand: any) :any => { + public getDefUnathenticatedRole = (brand: any): any => { return this.getRoleWithName(brand.roles, this.getConfigRole(ConfigService.getBrand(brand.name, 'auth').defaultRole).name); } - public getRolesWithBrand = (brand) :Observable => { - return super.getObservable(Role.find({branding:brand.id}).populate('users')); + public getRolesWithBrand = (brand): Observable => { + return super.getObservable(Role.find({ branding: brand.id }).populate('users')); } public getRoleIds = (fromRoles, roleNames) => { sails.log.verbose("Getting id of role names..."); - return _.map(_.filter(fromRoles, (role)=> {return _.includes(roleNames, role.name)}), 'id'); + return _.map(_.filter(fromRoles, (role) => { return _.includes(roleNames, role.name) }), 'id'); } public async createRoleWithBrand(brand, roleName) { - let roleConfig = + let roleConfig = { name: roleName, branding: brand.id }; - sails.log.verbose('createRoleWithBrand - brand.id '+brand.id); - let rolesResp:any = {}; - let rolesRespPromise = await this.getRolesWithBrand(brand).flatMap(roles => { + sails.log.verbose('createRoleWithBrand - brand.id ' + brand.id); + let rolesResp: any = {}; + let rolesRespPromise = await firstValueFrom(this.getRolesWithBrand(brand).pipe(flatMap(roles => { _.map(roles, (role) => { if (_.isEmpty(rolesResp.roles)) { rolesResp.roles = []; } rolesResp.roles.push(role); }); - return Observable.of(rolesResp); - }).first().toPromise(); - + return of(rolesResp); + }), first())); + sails.log.verbose(rolesRespPromise); - let roleToCreate = _.find(rolesRespPromise.roles, ['name',roleName]); - if(_.isUndefined(roleToCreate)) { - sails.log.verbose('createRoleWithBrand - roleConfig '+JSON.stringify(roleConfig)); + let roleToCreate = _.find(rolesRespPromise.roles, ['name', roleName]); + if (_.isUndefined(roleToCreate)) { + sails.log.verbose('createRoleWithBrand - roleConfig ' + JSON.stringify(roleConfig)); let newRole = await Role.create(roleConfig); sails.log.verbose("createRoleWithBrand - adding role to brand " + newRole.id); const q = BrandingConfig.addToCollection(brand.id, 'roles').members([newRole.id]); - return await super.getObservable(q, 'exec', 'simplecb').toPromise(); + return await firstValueFrom(super.getObservable(q, 'exec', 'simplecb')); } else { - sails.log.verbose('createRoleWithBrand - role ' +roleName + ' exists'); - return Observable.of(brand); + sails.log.verbose('createRoleWithBrand - role ' + roleName + ' exists'); + return of(brand); } } @@ -142,10 +143,10 @@ export module Services { var adminRole = this.getAdmin(defBrand); if (adminRole == null) { sails.log.verbose("Creating default admin, and other roles..."); - return Observable.from(this.getConfigRoles()) - .flatMap(roleConfig => { + return from(this.getConfigRoles()) + .pipe(flatMap(roleConfig => { return super.getObservable(Role.create(roleConfig)) - .flatMap(newRole => { + .pipe(flatMap(newRole => { sails.log.verbose("Adding role to brand:" + newRole.id); var brand:BrandingModel = sails.services.brandingservice.getDefault(); // START Sails 1.0 upgrade @@ -154,23 +155,23 @@ export module Services { // return super.getObservable(brand, 'save', 'simplecb'); return super.getObservable(q, 'exec', 'simplecb'); // END Sails 1.0 upgrade - }); - }) - .last() - .flatMap(brand => { + })); + }), + last(), + flatMap(brand => { return sails.services.brandingservice.loadAvailableBrands(); - }); + })); } else { sails.log.verbose("Admin role exists."); - return Observable.of(defBrand); + return of(defBrand); } } protected getConfigRole = (roleName) => { - return _.find(sails.config.auth.roles, (o) => {return o.name == roleName}); + return _.find(sails.config.auth.roles, (o) => { return o.name == roleName }); } - protected getConfigRoles = (roleProp=null, customObj=null) => { + protected getConfigRoles = (roleProp = null, customObj = null) => { var retVal = sails.config.auth.roles; if (roleProp) { retVal = [] diff --git a/typescript/api/services/TranslationService.ts b/typescript/api/services/TranslationService.ts index fb23a74c5f..f04f24bf75 100644 --- a/typescript/api/services/TranslationService.ts +++ b/typescript/api/services/TranslationService.ts @@ -17,7 +17,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; import {Services as services} from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; import i18next from "i18next" diff --git a/typescript/api/services/TriggerService.ts b/typescript/api/services/TriggerService.ts index 19bbb0851f..e99b27da74 100644 --- a/typescript/api/services/TriggerService.ts +++ b/typescript/api/services/TriggerService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, of, from } from 'rxjs'; +import { concatMap, last } from 'rxjs/operators'; import { RBValidationError, BrandingModel, @@ -25,7 +26,7 @@ import { PopulateExportedMethods, } from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; -import { default as moment } from 'moment'; +import moment from '../shims/momentShim'; import numeral from 'numeral'; declare var sails: Sails; @@ -75,7 +76,7 @@ export module Services { _.set(record, "metaMetadata.form", _.get(options, "targetForm", record.metaMetadata.form)); } - return Observable.of(record); + return of(record); } /** @@ -116,14 +117,14 @@ export module Services { }); if (!_.isEmpty(hookFnDefArray)) { sails.log.debug(`runHooksSync, running..`); - return Observable.from(hookFnDefArray) - .concatMap(hookDef => { + return from(hookFnDefArray) + .pipe(concatMap(hookDef => { return hookDef.hookFn(oid, record, hookDef.hookOpt, user); }) - .last(); + ,last()); } else { sails.log.debug(`runHooksSync, no observables to run`); - return Observable.of(record); + return of(record); } } diff --git a/typescript/api/services/UsersService.ts b/typescript/api/services/UsersService.ts index 1e21114d1b..b9987b4589 100644 --- a/typescript/api/services/UsersService.ts +++ b/typescript/api/services/UsersService.ts @@ -17,9 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { - Observable -} from 'rxjs/Rx'; +import { Observable, of, from, throwError, lastValueFrom, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap, map, last } from 'rxjs/operators'; import { isObservable @@ -390,13 +389,11 @@ export module Services { } private resolveHookResponse(hookResponse) { - let response = hookResponse; if (isObservable(hookResponse)) { - response = hookResponse.toPromise(); + return firstValueFrom(hookResponse); } else { - response = Promise.resolve(hookResponse); + return Promise.resolve(hookResponse); } - return response; } protected aafAuthInit = () => { @@ -983,7 +980,7 @@ export module Services { } sails.log.verbose("Default user missing, creating..."); return super.getObservable(User.create(defaultUser)) - .flatMap(defUser => { + .pipe(flatMap(defUser => { // START Sails 1.0 upgrade const defRoleIds = _.map(defRoles, (o) => { return o.id; @@ -991,27 +988,27 @@ export module Services { let q = User.addToCollection(defUser.id, 'roles').members(defRoleIds); // END Sails 1.0 upgrade return super.getObservable(q, 'exec', 'simplecb') - .flatMap(dUser => { - return Observable.from(defRoles) - .map(roleObserved => { + .pipe(flatMap(dUser => { + return from(defRoles) + .pipe(map(roleObserved => { let role: any = roleObserved; // START Sails 1.0 upgrade // role.users.add(defUser.id) q = Role.addToCollection(role.id, 'users').members([defUser.id]); // END Sails 1.0 upgrade return super.getObservable(q, 'exec', 'simplecb'); - }); + })); }) - .last() - .flatMap(lastRole => { - return Observable.of({ + ,last() + ,flatMap(lastRole => { + return of({ defUser: defUser, defRoles: defRoles }); - }); - }); + })); + })); } else { - return Observable.of({ + return of({ defUser: defaultUser, defRoles: defRoles }); @@ -1034,7 +1031,7 @@ export module Services { // ignore audit events for users with no user, which had crashed the app when user has already logged out if (_.isEmpty(user)) { sails.log.verbose('No user to audit, ignoring: ' + action); - return Observable.of(null).toPromise(); + return firstValueFrom(of(null)); } let auditEvent = {}; if (!_.isEmpty(user.password)) { @@ -1046,7 +1043,7 @@ export module Services { auditEvent['additionalContext'] = this.stringifyObject(additionalContext); sails.log.verbose('Adding user audit event'); sails.log.verbose(auditEvent); - return super.getObservable(UserAudit.create(auditEvent)).toPromise(); + return firstValueFrom(super.getObservable(UserAudit.create(auditEvent))); } stringifyObject(object: any): any { @@ -1068,13 +1065,13 @@ export module Services { var usernameField = authConfig.local.usernameField, passwordField = authConfig.local.passwordField; - return this.getUserWithUsername(username).flatMap(user => { + return this.getUserWithUsername(username).pipe(flatMap(user => { if (user) { - return Observable.throw(new Error(`Username already exists`)); + return throwError(new Error(`Username already exists`)); } else { - return this.findUsersWithEmail(email, null, null).flatMap(emailCheck => { + return this.findUsersWithEmail(email, null, null).pipe(flatMap(emailCheck => { if (_.size(emailCheck) > 0) { - return Observable.throw(new Error(`Email already exists, it must be unique`)); + return throwError(new Error(`Email already exists, it must be unique`)); } else { var newUser = { type: 'local', @@ -1087,9 +1084,9 @@ export module Services { newUser[passwordField] = password; return super.getObservable(User.create(newUser)); } - }); + })); } - }); + })); } @@ -1111,14 +1108,14 @@ export module Services { var usernameField = defAuthConfig.local.usernameField, passwordField = defAuthConfig.local.passwordField; var defAdminRole = RolesService.getAdminFromRoles(defRoles); - return Observable.of(defAdminRole) - .flatMap(defAdminRole => { + return of(defAdminRole) + .pipe(flatMap(defAdminRole => { this.localAuthInit(); this.aafAuthInit(); this.openIdConnectAuth(); this.bearerTokenAuthInit(); return this.initDefAdmin(defRoles, defAdminRole); - }); + })); } public getUserWithUsername = (username) => { @@ -1142,7 +1139,7 @@ export module Services { public setUserKey = (userid, uuid) => { const uuidHash = _.isEmpty(uuid) ? uuid : crypto.createHash('sha256').update(uuid).digest('base64'); - return this.getUserWithId(userid).flatMap(user => { + return this.getUserWithId(userid).pipe(flatMap(user => { if (user) { const q = User.update({ id: userid @@ -1151,15 +1148,15 @@ export module Services { }); return this.getObservable(q, 'exec', 'simplecb'); } else { - return Observable.throw(new Error('No such user with id:' + userid)); + return throwError(new Error('No such user with id:' + userid)); } - }); + })); } public updateUserDetails = (userid, name, email, password): Observable => { const authConfig = ConfigService.getBrand(BrandingService.getDefault().name, 'auth'); var passwordField = authConfig.local.passwordField; - return this.getUserWithId(userid).flatMap(user => { + return this.getUserWithId(userid).pipe(flatMap(user => { if (user) { const update = { name: name @@ -1184,25 +1181,25 @@ export module Services { }, update); return this.getObservable(q, 'exec', 'simplecb'); } else { - return Observable.throw(new Error('No such user with id:' + userid)); + return throwError(new Error('No such user with id:' + userid)); } - }); + })); } public updateUserRoles = (userid, newRoleIds): Observable => { - return this.getUserWithId(userid).flatMap(user => { + return this.getUserWithId(userid).pipe(flatMap(user => { if (user) { if (_.isEmpty(newRoleIds) || newRoleIds.length == 0) { - return Observable.throw(new Error('Please assign at least one role')); + return throwError(new Error('Please assign at least one role')); } // START Sails 1.0 upgrade const q = User.replaceCollection(user.id, 'roles').members(newRoleIds); // END Sails 1.0 upgrade return this.getObservable(q, 'exec', 'simplecb'); } else { - return Observable.throw(new Error('No such user with id:' + userid)); + return throwError(new Error('No such user with id:' + userid)); } - }); + })); } private updateUserAfterLogin(user,done){ @@ -1255,7 +1252,7 @@ export module Services { query['type'] = source; } return this.getObservable(User.find(query).populate('roles')) - .flatMap(users => { + .pipe(flatMap(users => { if (brandId) { _.remove(users, (user) => { const isInBrand = _.find(user.roles, (role) => { @@ -1264,8 +1261,8 @@ export module Services { return !isInBrand; }); } - return Observable.of(users); - }); + return of(users); + })); } /** diff --git a/typescript/api/services/VocabService.ts b/typescript/api/services/VocabService.ts index 5033362161..d0117477ff 100644 --- a/typescript/api/services/VocabService.ts +++ b/typescript/api/services/VocabService.ts @@ -17,7 +17,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; +import { Observable, of, from, zip, throwError } from 'rxjs'; +import { mergeMap as flatMap, last, map, concatAll, concatMap, delay } from 'rxjs/operators'; import { SearchService, VocabQueryConfig, BrandingModel, Services as services } from '@researchdatabox/redbox-core-types'; import { Sails } from 'sails'; import axios from 'axios'; @@ -51,12 +52,13 @@ export module Services { public bootstrap() { return _.isEmpty(sails.config.vocab.bootStrapVocabs) ? - Observable.of(null) - : Observable.from(sails.config.vocab.bootStrapVocabs) - .flatMap(vocabId => { - return this.getVocab(vocabId); - }) - .last(); + of(null) + : from(sails.config.vocab.bootStrapVocabs).pipe( + flatMap(vocabId => { + return this.getVocab(vocabId); + }), + last() + ); } public async findInMintTriggerWrapper(user: object, options: object, failureMode: string) { @@ -303,48 +305,53 @@ export module Services { public getVocab = (vocabId): Observable => { // Check cache - return CacheService.get(vocabId).flatMap(data => { - if (data) { - sails.log.verbose(`Returning cached vocab: ${vocabId}`); - return Observable.of(data); - } - if (sails.config.vocab.nonAnds && sails.config.vocab.nonAnds[vocabId]) { - return this.getNonAndsVocab(vocabId); - } - const url = `${sails.config.vocab.rootUrl}${vocabId}/${sails.config.vocab.conceptUri}`; - let items = null; // a flat array containing all the entries - const rawItems = []; - return this.getConcepts(url, rawItems).flatMap(allRawItems => { - // // we only are interested in notation, label and the uri - items = _.map(allRawItems, rawItem => { - return { uri: rawItem._about, notation: rawItem.notation, label: rawItem.prefLabel._value }; - }); - CacheService.set(vocabId, items); - return Observable.of(items); - }); - }); + return from(CacheService.get(vocabId)).pipe( + flatMap(data => { + if (data) { + sails.log.verbose(`Returning cached vocab: ${vocabId}`); + return of(data); + } + if (sails.config.vocab.nonAnds && sails.config.vocab.nonAnds[vocabId]) { + return this.getNonAndsVocab(vocabId); + } + const url = `${sails.config.vocab.rootUrl}${vocabId}/${sails.config.vocab.conceptUri}`; + let items = null; // a flat array containing all the entries + const rawItems = []; + return this.getConcepts(url, rawItems).pipe( + flatMap(allRawItems => { + // we only are interested in notation, label and the uri + items = _.map(allRawItems, rawItem => { + return { uri: rawItem._about, notation: rawItem.notation, label: rawItem.prefLabel._value }; + }); + CacheService.set(vocabId, items); + return of(items); + }) + ); + }) + ); } // have to do this since ANDS endpoint ignores _pageSize protected getConcepts(url, rawItems) { console.log(`Getting concepts....${url}`); - return Observable.fromPromise(axios.get(url)) - .flatMap((resp) => { + return from(axios.get(url)).pipe( + flatMap((resp) => { let response: any = resp.data; rawItems = rawItems.concat(response.result.items); if (response.result && response.result.next) { return this.getConcepts(response.result.next, rawItems); } - return Observable.of(rawItems); - }); + return of(rawItems); + }) + ); } protected getNonAndsVocab(vocabId) { const url = sails.config.vocab.nonAnds[vocabId].url; - return Observable.fromPromise(axios.get(url)).flatMap(response => { + return from(axios.get(url)).pipe(flatMap(response => { CacheService.set(vocabId, response.data); - return Observable.of(response); - }); + return of(response); + })); } loadCollection(collectionId, progressId = null, force = false) { @@ -352,14 +359,14 @@ export module Services { const bufferCount = sails.config.vocab.collection[collectionId].processingBuffer; const processWindow = sails.config.vocab.collection[collectionId].processingTime; let collectionData = null; - return this[getMethod](collectionId).flatMap(data => { + return this[getMethod](collectionId).pipe(flatMap(data => { if (_.isEmpty(data) || force) { // return a receipt and then start the process of loading... const url = sails.config.vocab.collection[collectionId].url; sails.log.verbose(`Loading collection: ${collectionId}, using url: ${url}`); const methodName = sails.config.vocab.collection[collectionId].saveMethod; - return Observable.fromPromise(axios.get(url)) - .flatMap(resp => { + return from(axios.get(url)).pipe( + flatMap(resp => { let response: any = resp.data; sails.log.verbose(`Got response retrieving data for collection: ${collectionId}, saving...`); sails.log.verbose(`Number of items: ${response.length}`); @@ -368,32 +375,34 @@ export module Services { // sails.log.verbose(collectionData); const updateObj = { currentIdx: 0, targetIdx: collectionData.length }; return AsynchsService.update({ id: progressId }, updateObj); - }) - .flatMap(updateResp => { + }), + flatMap(updateResp => { sails.log.verbose(`Updated asynch progress...`); - return Observable.from(collectionData); - }) - .map((buffer, i) => { - setTimeout(() => { - sails.log.verbose(`Processing chunk: ${i}`); - return this.saveCollectionChunk(methodName, buffer, i) - .flatMap(saveResp => { - sails.log.verbose(`Updating chunk progress...${i}`); - if (i == collectionData.length) { - sails.log.verbose(`Asynch completed.`); - return AsynchsService.finish(progressId); - } else { - return AsynchsService.update({ id: progressId }, { currentIdx: i + 1, status: 'processing' }); - } - }); - }, i * processWindow); + return from(collectionData); + }), + concatMap((buffer, i) => { + sails.log.verbose(`Processing chunk: ${i}`); + return of(buffer).pipe( + delay(i * processWindow), + flatMap(() => this.saveCollectionChunk(methodName, buffer, i).pipe( + flatMap(saveResp => { + sails.log.verbose(`Updating chunk progress...${i}`); + if (i === collectionData.length - 1) { + sails.log.verbose(`Asynch completed.`); + return AsynchsService.finish(progressId); + } else { + return AsynchsService.update({ id: progressId }, { currentIdx: i + 1, status: 'processing' }); + } + }) + )) + ); }) - .concat() + ) } else { sails.log.verbose(`Collection already loaded: ${collectionId}`); - return Observable.of(null); + return of(null); } - }); + })); } protected saveCollectionChunk(methodName, buffer, i) { @@ -406,10 +415,10 @@ export module Services { public rvaGetResourceDetails(uri, vocab) { const url = sails.config.vocab.rootUrl + `${vocab}/resource.json?uri=${uri}`; - return Observable.fromPromise(axios.get(url)).flatMap(response => { + return from(axios.get(url)).pipe(flatMap(response => { CacheService.set(vocab, response.data); - return Observable.of(response); - }); + return of(response); + })); } protected getMintOptions(url, method, contentType = 'application/json; charset=utf-8') { diff --git a/typescript/api/services/WorkflowStepsService.ts b/typescript/api/services/WorkflowStepsService.ts index bf54cc403d..5867d9a1c9 100644 --- a/typescript/api/services/WorkflowStepsService.ts +++ b/typescript/api/services/WorkflowStepsService.ts @@ -17,8 +17,9 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; -import {Services as services} from '@researchdatabox/redbox-core-types'; +import { Observable, zip, from, of, firstValueFrom } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; +import { Services as services } from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; declare var sails: Sails; @@ -83,7 +84,7 @@ export module Services { if(form == '') { _.set(workflowConf.config,'form','generated-view-only'); } - var obs = await this.create(workflowStep["recordType"], workflowStep["workflow"], workflowConf.config, workflowConf.starting == true, workflowConf['hidden']).toPromise(); + var obs = await firstValueFrom(this.create(workflowStep["recordType"], workflowStep["workflow"], workflowConf.config, workflowConf.starting == true, workflowConf['hidden'])); workflowSteps.push(obs); }; } @@ -95,7 +96,7 @@ export module Services { - public create(recordType, name, workflowConf, starting, hidden:boolean = false) { + public create(recordType, name, workflowConf, starting, hidden: boolean = false) { return super.getObservable(WorkflowStep.create({ name: name, config: workflowConf, @@ -106,15 +107,15 @@ export module Services { } public get(recordType, name) { - return super.getObservable(WorkflowStep.findOne({recordType: recordType.id, name: name })); + return super.getObservable(WorkflowStep.findOne({ recordType: recordType.id, name: name })); } public getAllForRecordType(recordType) { - return super.getObservable(WorkflowStep.find({recordType: recordType.id, hidden: { '!=': true } })); + return super.getObservable(WorkflowStep.find({ recordType: recordType.id, hidden: { '!=': true } })); } public getFirst(recordType) { - return super.getObservable(WorkflowStep.findOne({recordType: recordType.id, starting: true })); + return super.getObservable(WorkflowStep.findOne({ recordType: recordType.id, starting: true })); } } } diff --git a/typescript/api/services/WorkspaceAsyncService.ts b/typescript/api/services/WorkspaceAsyncService.ts index 28339a9fa2..3982463aa7 100644 --- a/typescript/api/services/WorkspaceAsyncService.ts +++ b/typescript/api/services/WorkspaceAsyncService.ts @@ -1,9 +1,9 @@ -import { Observable } from 'rxjs/Rx'; +import { Observable } from 'rxjs'; import {Services as services} from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; const util = require('util'); -const moment = require('moment'); +import { DateTime } from 'luxon'; declare var sails: Sails; declare var _this; @@ -49,7 +49,7 @@ export module Services { public update(id, obj) { if(obj.status === 'finished'){ - obj.date_completed = moment().format('YYYY-MM-DD HH:mm:ss'); + obj.date_completed = DateTime.local().toFormat('yyyy-LL-dd HH:mm:ss'); } return super.getObservable( WorkspaceAsync.update({id: id}, obj) diff --git a/typescript/api/services/WorkspaceService.ts b/typescript/api/services/WorkspaceService.ts index 8209f5d25d..0f80060d65 100644 --- a/typescript/api/services/WorkspaceService.ts +++ b/typescript/api/services/WorkspaceService.ts @@ -1,4 +1,4 @@ -import { Observable } from 'rxjs/Rx'; +import { Observable, from } from 'rxjs'; import {Services as services} from '@researchdatabox/redbox-core-types'; import { Sails, Model } from "sails"; import axios from 'axios'; @@ -83,11 +83,11 @@ export module Services { */ public async getWorkspaces(targetRecordOid: string, targetRecord:any = undefined) { if (_.isUndefined(targetRecord)) { - targetRecord = await RecordsService.getMeta(targetRecordOid).toPromise(); + targetRecord = await RecordsService.getMeta(targetRecordOid); } const workspaces = []; _.each(_.get(targetRecord, 'metadata.workspaces'), async (workspaceInfo:any) => { - workspaces.push(await RecordsService.getMeta(workspaceInfo.id).toPromise()); + workspaces.push(await RecordsService.getMeta(workspaceInfo.id)); }); return workspaces; } @@ -111,7 +111,7 @@ export module Services { }, headers: config.redboxHeaders }; - return Observable.fromPromise(axios(post)); + return from(axios(post)); } getRecordMeta(config: any, rdmp: string) { @@ -120,7 +120,7 @@ export module Services { url: config.brandingAndPortalUrl + '/api/records/metadata/' + rdmp, headers: config.redboxHeaders }; - return Observable.fromPromise(axios(get)); + return from(axios(get)); } updateRecordMeta(config: any, record: any, id: string) { @@ -130,7 +130,7 @@ export module Services { data: record, headers: config.redboxHeaders }; - return Observable.fromPromise(axios(post)); + return from(axios(post)); } userInfo(userId: string) { diff --git a/typescript/api/services/WorkspaceTypesService.ts b/typescript/api/services/WorkspaceTypesService.ts index 3e21414e82..5f69023cdf 100644 --- a/typescript/api/services/WorkspaceTypesService.ts +++ b/typescript/api/services/WorkspaceTypesService.ts @@ -17,9 +17,10 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import { Observable } from 'rxjs/Rx'; -import {Services as services} from '@researchdatabox/redbox-core-types'; -import {Sails, Model} from "sails"; +import { zip, of } from 'rxjs'; +import { mergeMap as flatMap } from 'rxjs/operators'; +import { Services as services } from '@researchdatabox/redbox-core-types'; +import { Sails, Model } from "sails"; declare var sails: Sails; declare var WorkspaceType: Model; @@ -43,7 +44,7 @@ export module Services { ]; public bootstrap = (defBrand) => { - return super.getObservable(WorkspaceType.destroy({branding: defBrand.id})).flatMap(whatever => { + return super.getObservable(WorkspaceType.destroy({ branding: defBrand.id })).pipe(flatMap(whatever => { const obsArr = []; sails.log.debug('WorkspaceTypes::Bootstrap'); sails.log.debug(sails.config.workspacetype); @@ -65,10 +66,10 @@ export module Services { if (_.isEmpty(obsArr)) { sails.log.verbose("Default or no workspaceTypes definition(s)."); } else { - return Observable.zip(...obsArr); + return zip(...obsArr); } - return Observable.of(obsArr); - }); + return of(obsArr); + })); } public create(brand, workspaceType) { @@ -86,11 +87,11 @@ export module Services { } public get(brand) { - return super.getObservable(WorkspaceType.find({branding: brand.id})); + return super.getObservable(WorkspaceType.find({ branding: brand.id })); } public getOne(brand, name) { - return super.getObservable(WorkspaceType.findOne({branding: brand.id, name: name})); + return super.getObservable(WorkspaceType.findOne({ branding: brand.id, name: name })); } } } diff --git a/typescript/api/shims/momentShim.ts b/typescript/api/shims/momentShim.ts new file mode 100644 index 0000000000..281c192177 --- /dev/null +++ b/typescript/api/shims/momentShim.ts @@ -0,0 +1,55 @@ +// A minimal Moment.js-compatible shim powered by Luxon for server-side templates and simple usage. +// Supports: moment(input).format(fmt), where fmt uses Moment tokens; input can be Date | number(ms) | ISO string. +// Note: Luxon tokens differ from Moment; we map common tokens here. + +import { DateTime } from 'luxon'; + +export function mapMomentToLuxonFormat(fmt: string): string { + if (!fmt) return fmt; + return fmt + // years + .replace(/YYYY/g, 'yyyy').replace(/YY/g, 'yy') + // months + .replace(/MMMM/g, 'LLLL').replace(/MMM/g, 'LLL').replace(/\bMM\b/g, 'LL').replace(/\bM\b/g, 'L') + // days + .replace(/\bDD\b/g, 'dd').replace(/\bD\b/g, 'd') + // weekday + .replace(/dddd/g, 'cccc').replace(/ddd/g, 'ccc') + // hours/minutes/seconds + .replace(/\bHH\b/g, 'HH').replace(/\bH\b/g, 'H') + .replace(/\bhh\b/g, 'hh').replace(/\bh\b/g, 'h') + .replace(/\bmm\b/g, 'mm').replace(/\bm\b/g, 'm') + .replace(/\bss\b/g, 'ss').replace(/\bs\b/g, 's') + // AM/PM + .replace(/A/g, 'a'); +} + +function toDateTime(input: any): DateTime { + if (input instanceof Date) return DateTime.fromJSDate(input); + if (typeof input === 'number') return DateTime.fromMillis(input); + if (typeof input === 'string') { + const iso = DateTime.fromISO(input, { setZone: true }); + if (iso.isValid) return iso; + const http = DateTime.fromHTTP(input); + if (http.isValid) return http; + const rfc = DateTime.fromRFC2822(input); + if (rfc.isValid) return rfc; + } + return DateTime.invalid('Unparsable date'); +} + +export function momentShim(input?: any) { + let dt = input ? toDateTime(input) : DateTime.local(); + const api: any = { + format(fmt?: string) { + if (!dt.isValid) return ''; + if (!fmt) return dt.toISO(); + if (fmt === 'L') return dt.toLocaleString(DateTime.DATE_SHORT); + const mapped = mapMomentToLuxonFormat(fmt); + return dt.toFormat(mapped); + } + }; + return api; +} + +export default momentShim; \ No newline at end of file diff --git a/api/helpers/readme.md b/typescript/helpers/readme.md similarity index 100% rename from api/helpers/readme.md rename to typescript/helpers/readme.md diff --git a/views/default/default/record/layout.ejs b/views/default/default/record/layout.ejs index 3cfe837db4..749ed83296 100644 --- a/views/default/default/record/layout.ejs +++ b/views/default/default/record/layout.ejs @@ -17,8 +17,7 @@ - - + diff --git a/views/layout.ejs b/views/layout.ejs index d6bf1c5b91..bf0eecadd7 100644 --- a/views/layout.ejs +++ b/views/layout.ejs @@ -17,8 +17,7 @@ - - +