diff --git a/.dependency-cruiser.js b/.dependency-cruiser.js new file mode 100644 index 00000000..2f32d16b --- /dev/null +++ b/.dependency-cruiser.js @@ -0,0 +1,476 @@ +/** @type {import('dependency-cruiser').IConfiguration} */ +module.exports = { + forbidden: [ + { + name: 'no-circular', + severity: 'warn', + comment: + 'This dependency is part of a circular relationship. You might want to revise ' + + 'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ', + from: {}, + to: { + circular: true + } + }, + { + name: 'no-orphans', + comment: + "This is an orphan module - it's likely not used (anymore?). Either use it or " + + "remove it. If it's logical this module is an orphan (i.e. it's a config file), " + + "add an exception for it in your dependency-cruiser configuration. By default " + + "this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " + + "files (.d.ts), tsconfig.json and some of the babel and webpack configs.", + severity: 'warn', + from: { + orphan: true, + pathNot: [ + '(^|/)[.][^/]+[.](js|cjs|mjs|ts|json)$', // dot files + '[.]d[.]ts$', // TypeScript declaration files + '(^|/)tsconfig[.]json$', // TypeScript config + '(^|/)(babel|webpack)[.]config[.](js|cjs|mjs|ts|json)$' // other configs + ] + }, + to: {}, + }, + { + name: 'no-deprecated-core', + comment: + 'A module depends on a node core module that has been deprecated. Find an alternative - these are ' + + "bound to exist - node doesn't deprecate lightly.", + severity: 'warn', + from: {}, + to: { + dependencyTypes: [ + 'core' + ], + path: [ + '^(v8/tools/codemap)$', + '^(v8/tools/consarray)$', + '^(v8/tools/csvparser)$', + '^(v8/tools/logreader)$', + '^(v8/tools/profile_view)$', + '^(v8/tools/profile)$', + '^(v8/tools/SourceMap)$', + '^(v8/tools/splaytree)$', + '^(v8/tools/tickprocessor-driver)$', + '^(v8/tools/tickprocessor)$', + '^(node-inspect/lib/_inspect)$', + '^(node-inspect/lib/internal/inspect_client)$', + '^(node-inspect/lib/internal/inspect_repl)$', + '^(async_hooks)$', + '^(punycode)$', + '^(domain)$', + '^(constants)$', + '^(sys)$', + '^(_linklist)$', + '^(_stream_wrap)$' + ], + } + }, + { + name: 'not-to-deprecated', + comment: + 'This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later ' + + 'version of that module, or find an alternative. Deprecated modules are a security risk.', + severity: 'warn', + from: {}, + to: { + dependencyTypes: [ + 'deprecated' + ] + } + }, + { + name: 'no-non-package-json', + severity: 'error', + comment: + "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " + + "That's problematic as the package either (1) won't be available on live (2 - worse) will be " + + "available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " + + "in your package.json.", + from: {}, + to: { + dependencyTypes: [ + 'npm-no-pkg', + 'npm-unknown' + ] + } + }, + { + name: 'not-to-unresolvable', + comment: + "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " + + 'module: add it to your package.json. In all other cases you likely already know what to do.', + severity: 'error', + from: {}, + to: { + couldNotResolve: true + } + }, + { + name: 'no-duplicate-dep-types', + comment: + "Likely this module depends on an external ('npm') package that occurs more than once " + + "in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " + + "maintenance problems later on.", + severity: 'warn', + from: {}, + to: { + moreThanOneDependencyType: true, + // as it's pretty common to have a type import be a type only import + // _and_ (e.g.) a devDependency - don't consider type-only dependency + // types for this rule + dependencyTypesNot: ["type-only"] + } + }, + + /* rules you might want to tweak for your specific situation: */ + + { + name: 'not-to-spec', + comment: + 'This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. ' + + "If there's something in a spec that's of use to other modules, it doesn't have that single " + + 'responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.', + severity: 'error', + from: {}, + to: { + path: '^(test|spec)\/.*[.](js|mjs|cjs|ts|ls|coffee|litcoffee|coffee[.]md)$' + } + }, + { + name: 'not-to-dev-dep', + severity: 'error', + comment: + "This module depends on an npm package from the 'devDependencies' section of your " + + 'package.json. It looks like something that ships to production, though. To prevent problems ' + + "with npm packages that aren't there on production declare it (only!) in the 'dependencies'" + + 'section of your package.json. If this module is development only - add it to the ' + + 'from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration', + from: { + path: '^(\.)', + pathNot: '^(test|spec|docs)\/.*[.](js|mjs|cjs|ts|ls|coffee|litcoffee|coffee[.]md)$' + }, + to: { + dependencyTypes: [ + 'npm-dev', + ], + // type only dependencies are not a problem as they don't end up in the + // production code or are ignored by the runtime. + dependencyTypesNot: [ + 'type-only' + ], + pathNot: [ + 'node_modules/@types/' + ] + } + }, + { + name: 'optional-deps-used', + severity: 'info', + comment: + "This module depends on an npm package that is declared as an optional dependency " + + "in your package.json. As this makes sense in limited situations only, it's flagged here. " + + "If you're using an optional dependency here by design - add an exception to your" + + "dependency-cruiser configuration.", + from: {}, + to: { + dependencyTypes: [ + 'npm-optional' + ] + } + }, + { + name: 'peer-deps-used', + comment: + "This module depends on an npm package that is declared as a peer dependency " + + "in your package.json. This makes sense if your package is e.g. a plugin, but in " + + "other cases - maybe not so much. If the use of a peer dependency is intentional " + + "add an exception to your dependency-cruiser configuration.", + severity: 'warn', + from: {}, + to: { + dependencyTypes: [ + 'npm-peer' + ] + } + } + ], + options: { + + /* conditions specifying which files not to follow further when encountered: + - path: a regular expression to match + - dependencyTypes: see https://github.com/sverweij/dependency-cruiser/blob/main/doc/rules-reference.md#dependencytypes-and-dependencytypesnot + for a complete list + */ + doNotFollow: { + path: 'node_modules' + }, + + /* conditions specifying which dependencies to exclude + - path: a regular expression to match + - dynamic: a boolean indicating whether to ignore dynamic (true) or static (false) dependencies. + leave out if you want to exclude neither (recommended!) + */ + exclude : { + path: "(wrapper|docs)", + dynamic: true + }, + + /* pattern specifying which files to include (regular expression) + dependency-cruiser will skip everything not matching this pattern + */ + // includeOnly : '', + + /* dependency-cruiser will include modules matching against the focus + regular expression in its output, as well as their neighbours (direct + dependencies and dependents) + */ + // focus : '', + + /* List of module systems to cruise. + When left out dependency-cruiser will fall back to the list of _all_ + module systems it knows of. It's the default because it's the safe option + It might come at a performance penalty, though. + moduleSystems: ['amd', 'cjs', 'es6', 'tsd'] + + As in practice only commonjs ('cjs') and ecmascript modules ('es6') + are widely used, you can limit the moduleSystems to those. + */ + + // moduleSystems: ['cjs', 'es6'], + + /* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/develop/' + to open it on your online repo or `vscode://file/${process.cwd()}/` to + open it in visual studio code), + */ + // prefix: '', + + /* false (the default): ignore dependencies that only exist before typescript-to-javascript compilation + true: also detect dependencies that only exist before typescript-to-javascript compilation + "specify": for each dependency identify whether it only exists before compilation or also after + */ + // tsPreCompilationDeps: false, + + /* + list of extensions to scan that aren't javascript or compile-to-javascript. + Empty by default. Only put extensions in here that you want to take into + account that are _not_ parsable. + */ + // extraExtensionsToScan: [".json", ".jpg", ".png", ".svg", ".webp"], + + /* if true combines the package.jsons found from the module up to the base + folder the cruise is initiated from. Useful for how (some) mono-repos + manage dependencies & dependency definitions. + */ + // combinedDependencies: false, + + /* if true leave symlinks untouched, otherwise use the realpath */ + // preserveSymlinks: false, + + /* TypeScript project file ('tsconfig.json') to use for + (1) compilation and + (2) resolution (e.g. with the paths property) + + The (optional) fileName attribute specifies which file to take (relative to + dependency-cruiser's current working directory). When not provided + defaults to './tsconfig.json'. + */ + tsConfig: { + fileName: 'jsconfig.json' + }, + + /* Webpack configuration to use to get resolve options from. + + The (optional) fileName attribute specifies which file to take (relative + to dependency-cruiser's current working directory. When not provided defaults + to './webpack.conf.js'. + + The (optional) `env` and `arguments` attributes contain the parameters to be passed if + your webpack config is a function and takes them (see webpack documentation + for details) + */ + // webpackConfig: { + // fileName: 'webpack.config.js', + // env: {}, + // arguments: {} + // }, + + /* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use + for compilation (and whatever other naughty things babel plugins do to + source code). This feature is well tested and usable, but might change + behavior a bit over time (e.g. more precise results for used module + systems) without dependency-cruiser getting a major version bump. + */ + // babelConfig: { + // fileName: '.babelrc', + // }, + + /* List of strings you have in use in addition to cjs/ es6 requires + & imports to declare module dependencies. Use this e.g. if you've + re-declared require, use a require-wrapper or use window.require as + a hack. + */ + // exoticRequireStrings: [], + /* options to pass on to enhanced-resolve, the package dependency-cruiser + uses to resolve module references to disk. You can set most of these + options in a webpack.conf.js - this section is here for those + projects that don't have a separate webpack config file. + + Note: settings in webpack.conf.js override the ones specified here. + */ + enhancedResolveOptions: { + /* List of strings to consider as 'exports' fields in package.json. Use + ['exports'] when you use packages that use such a field and your environment + supports it (e.g. node ^12.19 || >=14.7 or recent versions of webpack). + + If you have an `exportsFields` attribute in your webpack config, that one + will have precedence over the one specified here. + */ + exportsFields: ["exports"], + /* List of conditions to check for in the exports field. e.g. use ['imports'] + if you're only interested in exposed es6 modules, ['require'] for commonjs, + or all conditions at once `(['import', 'require', 'node', 'default']`) + if anything goes for you. Only works when the 'exportsFields' array is + non-empty. + + If you have a 'conditionNames' attribute in your webpack config, that one will + have precedence over the one specified here. + */ + conditionNames: ["import", "require", "node", "default"], + /* + The extensions, by default are the same as the ones dependency-cruiser + can access (run `npx depcruise --info` to see which ones that are in + _your_ environment. If that list is larger than what you need (e.g. + it contains .js, .jsx, .ts, .tsx, .cts, .mts - but you don't use + TypeScript you can pass just the extensions you actually use (e.g. + [".js", ".jsx"]). This can speed up the most expensive step in + dependency cruising (module resolution) quite a bit. + */ + // extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"], + /* + If your TypeScript project makes use of types specified in 'types' + fields in package.jsons of external dependencies, specify "types" + in addition to "main" in here, so enhanced-resolve (the resolver + dependency-cruiser uses) knows to also look there. You can also do + this if you're not sure, but still use TypeScript. In a future version + of dependency-cruiser this will likely become the default. + */ + mainFields: ["main", "types", "typings"], + /* + A list of alias fields in manifests (package.jsons). + Specify a field, such as browser, to be parsed according to + [this specification](https://github.com/defunctzombie/package-browser-field-spec). + Also see [resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealiasfields) + in the webpack docs. + + Defaults to an empty array (don't use any alias fields). + */ + // aliasFields: ["browser"], + }, + reporterOptions: { + dot: { + /* pattern of modules that can be consolidated in the detailed + graphical dependency graph. The default pattern in this configuration + collapses everything in node_modules to one folder deep so you see + the external modules, but not the innards your app depends upon. + */ + collapsePattern: 'node_modules/(@[^/]+/[^/]+|[^/]+)', + + /* Options to tweak the appearance of your graph.See + https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions + for details and some examples. If you don't specify a theme + don't worry - dependency-cruiser will fall back to the default one. + */ + // theme: { + // graph: { + // /* use splines: "ortho" for straight lines. Be aware though + // graphviz might take a long time calculating ortho(gonal) + // routings. + // */ + // splines: "true" + // }, + // modules: [ + // { + // criteria: { matchesFocus: true }, + // attributes: { + // fillcolor: "lime", + // penwidth: 2, + // }, + // }, + // { + // criteria: { matchesFocus: false }, + // attributes: { + // fillcolor: "lightgrey", + // }, + // }, + // { + // criteria: { matchesReaches: true }, + // attributes: { + // fillcolor: "lime", + // penwidth: 2, + // }, + // }, + // { + // criteria: { matchesReaches: false }, + // attributes: { + // fillcolor: "lightgrey", + // }, + // }, + // { + // criteria: { source: "^src/model" }, + // attributes: { fillcolor: "#ccccff" } + // }, + // { + // criteria: { source: "^src/view" }, + // attributes: { fillcolor: "#ccffcc" } + // }, + // ], + // dependencies: [ + // { + // criteria: { "rules[0].severity": "error" }, + // attributes: { fontcolor: "red", color: "red" } + // }, + // { + // criteria: { "rules[0].severity": "warn" }, + // attributes: { fontcolor: "orange", color: "orange" } + // }, + // { + // criteria: { "rules[0].severity": "info" }, + // attributes: { fontcolor: "blue", color: "blue" } + // }, + // { + // criteria: { resolved: "^src/model" }, + // attributes: { color: "#0000ff77" } + // }, + // { + // criteria: { resolved: "^src/view" }, + // attributes: { color: "#00770077" } + // } + // ] + // } + }, + archi: { + /* pattern of modules that can be consolidated in the high level + graphical dependency graph. If you use the high level graphical + dependency graph reporter (`archi`) you probably want to tweak + this collapsePattern to your situation. + */ + collapsePattern: '^(packages|src|lib|app|bin|test(s?)|spec(s?))/[^/]+|node_modules/(@[^/]+/[^/]+|[^/]+)', + + /* Options to tweak the appearance of your graph.See + https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions + for details and some examples. If you don't specify a theme + for 'archi' dependency-cruiser will use the one specified in the + dot section (see above), if any, and otherwise use the default one. + */ + // theme: { + // }, + }, + "text": { + "highlightFocused": true + }, + } + } +}; +// generated: dependency-cruiser@16.0.0 on 2024-01-19T10:44:28.622Z diff --git a/.env.template b/.env.template index 93d40865..ddf43b82 100644 --- a/.env.template +++ b/.env.template @@ -1,6 +1,6 @@ # MongoDB -MONGODB_NAME="raccoon-dicom" -MONGODB_HOSTS=["mongodb"] +MONGODB_NAME="raccoon" +MONGODB_HOSTS=["127.0.0.1"] MONGODB_PORTS=[27017] MONGODB_USER="root" MONGODB_PASSWORD="root" @@ -8,6 +8,16 @@ MONGODB_AUTH_SOURCE="admin" MONGODB_OPTIONS="" MONGODB_IS_SHARDING_MODE=false +# SQL +SQL_HOST="127.0.0.1" +SQL_PORT="5432" +SQL_DB="raccoon" +SQL_TYPE="postgres" +SQL_USERNAME="postgres" +SQL_PASSWORD="postgres" +SQL_LOGGING=false +SQL_FORCE_SYNC=false + # Server SERVER_PORT=8081 SERVER_SESSION_SECRET_KEY="secret-key" diff --git a/.gitignore b/.gitignore index 36df3e98..069815de 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,7 @@ /config/logback.xml /pm2log/** !pm2log/.gitkeep -config/ae-prod.properties \ No newline at end of file +config/ae-prod.properties + +# ignore jscpd output report +/report \ No newline at end of file diff --git a/.jscpd.json b/.jscpd.json new file mode 100644 index 00000000..366bffd8 --- /dev/null +++ b/.jscpd.json @@ -0,0 +1,19 @@ +{ + "threshold": 0.1, + "reporters": [ + "html", + "console" + ], + "ignore": [ + "**/node_modules/**", + "models/DICOM/dcm4che/wrapper/**/*.ts", + "models/DICOM/dcm4che/wrapper/**/*.js", + "docs/**", + "test/**" + ], + "ignorePattern": [ + "const .* require.*" + ], + "absolute": true, + "gitignore": true +} \ No newline at end of file diff --git a/api/WADO-URI/controller/retrieveInstance.js b/api/WADO-URI/controller/retrieveInstance.js index f80342f3..75e1acf2 100644 --- a/api/WADO-URI/controller/retrieveInstance.js +++ b/api/WADO-URI/controller/retrieveInstance.js @@ -1,16 +1,18 @@ -const { WadoUriService, NotFoundInstanceError } = require("../service/WADO-URI.service"); +const { WadoUriService } = require("@api/WADO-URI/service/WADO-URI.service"); const { Controller } = require("../../controller.class"); const { ApiLogger } = require("../../../utils/logs/api-logger"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); class RetrieveSingleInstanceController extends Controller { constructor(req, res) { super(req, res); - this.service = new WadoUriService(req, res); this.logger = new ApiLogger(this.request, "WADO-URI"); + this.service = new WadoUriService(req, res, this.logger); } async mainProcess() { - let { + let { contentType } = this.request.query; @@ -18,27 +20,27 @@ class RetrieveSingleInstanceController extends Controller { try { - if (contentType === "application/dicom") { - this.service.getAndResponseDicomInstance(); + if (contentType === "application/dicom" || !contentType) { + + let dicomInstanceReadStream = await this.service.getDicomInstanceReadStream(); + this.response.setHeader("Content-Type", "application/dicom"); + dicomInstanceReadStream.pipe(this.response); } else if (contentType === "image/jpeg") { - this.service.getAndResponseJpeg(); - } else if (!contentType) { - this.service.getAndResponseDicomInstance(); + let jpegBuffer = await this.service.handleRequestQueryAndGetJpeg(); + + this.response.setHeader("Content-Type", "image/jpeg"); + + this.response.end(jpegBuffer, "buffer"); } - } catch(e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - this.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); + this.response.on("finish", () => this.service.auditInstanceTransferred()); + + } catch (e) { + this.service.auditInstanceTransferred(EventOutcomeIndicator.MajorFailure); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.logger, e); + return apiErrorArrayHandler.doErrorResponse(); } - + } } @@ -48,7 +50,7 @@ class RetrieveSingleInstanceController extends Controller { * @param {import("http").IncomingMessage} req * @param {import("http").ServerResponse} res */ -module.exports = async function(req, res) { +module.exports = async function (req, res) { let controller = new RetrieveSingleInstanceController(req, res); await controller.doPipeline(); diff --git a/api/WADO-URI/service/WADO-URI.service.js b/api/WADO-URI/service/WADO-URI.service.js index 05f83b2e..d40e8a6a 100644 --- a/api/WADO-URI/service/WADO-URI.service.js +++ b/api/WADO-URI/service/WADO-URI.service.js @@ -1,17 +1,20 @@ const mongoose = require("mongoose"); const fs = require("fs"); const _ = require("lodash"); -const renderedService = require("../../dicom-web/controller/WADO-RS/service/rendered.service"); +const { RenderedImageProcessParameterHandler, getInstanceFrameObj } = require("../../dicom-web/controller/WADO-RS/service/rendered.service"); const { Dcm2JpgExecutor } = require("../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor"); const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions"); -const sharp = require('sharp'); +const imageSizeOf = require("image-size"); +const { promisify } = require("util"); +const imageSizeOfPromise = promisify(imageSizeOf); const Magick = require("../../../models/magick"); const { NotFoundInstanceError, InvalidFrameNumberError, InstanceGoneError } = require("../../../error/dicom-instance"); -const dicomModel = require("../../../models/mongodb/models/dicom"); +const { InstanceModel } = require("@dbModels/instance.model"); const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); const { DicomWebService } = require("@root/api/dicom-web/service/dicom-web.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class WadoUriService { @@ -20,95 +23,17 @@ class WadoUriService { * @param {import("http").IncomingMessage} req * @param {import("http").ServerResponse} res */ - constructor(req, res) { + constructor(req, res, apiLogger) { this.request = req; this.response = res; + this.apiLogger = apiLogger; this.auditBeginTransferring(); - } - - async getAndResponseDicomInstance() { - try { - - let dicomInstanceReadStream = await this.getDicomInstanceReadStream(); - - this.response.setHeader("Content-Type", "application/dicom"); - dicomInstanceReadStream.pipe(this.response); - this.auditInstanceTransferred(); - - } catch (e) { - this.auditInstanceTransferred(EventOutcomeIndicator.MajorFailure); - - if (e instanceof NotFoundInstanceError) { - this.response.writeHead(404, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(); - } else if (e instanceof InstanceGoneError) { - this.response.writeHead(410, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify({ - Details: e.message, - HttpStatus: 410, - Message: "Image Gone", - Method: "GET" - })); - } - throw e; - } + let clonedRequestQuery = _.cloneDeep(this.request?.query); + _.set(clonedRequestQuery, "imageQuality", _.get(this.request.query, "imageQuality", "")); + this.renderedImageProcessParameterHandler = new RenderedImageProcessParameterHandler(clonedRequestQuery); } - async getAndResponseJpeg() { - try { - - let jpegBuffer = await this.handleRequestQueryAndGetJpeg(); - - this.response.setHeader("Content-Type", "image/jpeg"); - - this.response.end(jpegBuffer, "buffer"); - this.auditInstanceTransferred(); - - } catch (e) { - this.auditInstanceTransferred(EventOutcomeIndicator.MajorFailure); - - if (e instanceof NotFoundInstanceError) { - - this.response.writeHead(404, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(); - - } else if (e instanceof InvalidFrameNumberError) { - - this.response.writeHead(400, { - "Content-Type": "application/dicom+json" - }); - - return this.response.end(JSON.stringify({ - Details: e.message, - HttpStatus: 400, - Message: "Bad request", - Method: "GET" - })); - - } else if (e instanceof InstanceGoneError) { - this.response.writeHead(410, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify({ - Details: e.message, - HttpStatus: 410, - Message: "Image Gone", - Method: "GET" - })); - } - - throw e; - } - } - - /** * @throws {NotFoundInstanceError} * @returns {Promise} @@ -127,7 +52,7 @@ class WadoUriService { objectUID: instanceUID } = this.request.query; - let imagePathObj = await dicomModel.getPathOfInstance({ + let imagePathObj = await InstanceModel.getPathOfInstance({ studyUID, seriesUID, instanceUID @@ -154,13 +79,13 @@ class WadoUriService { async handleRequestQueryAndGetJpeg() { let { - imageSharp, + imageSize, magick } = await this.handleFrameNumberAndGetImageObj(); await this.handleImageQuality(magick); - await this.handleRegion(this.request.query, imageSharp, magick); - await this.handleRowsAndColumns(this.request.query, imageSharp, magick); + await this.handleRegion(this.request.query, imageSize, magick); + await this.handleRowsAndColumns(this.request.query, imageSize, magick); await this.handleImageICCProfile(this.request.query, magick, this.request.query.objectUID); await magick.execCommand(); @@ -170,10 +95,7 @@ class WadoUriService { async handleFrameNumberAndGetImageObj() { let imagePathObj = await this.getDicomInstancePathObj(); - let instanceFramesObj = await renderedService.getInstanceFrameObj(imagePathObj, { - "00281050": 1, - "00281051": 1 - }); + let instanceFramesObj = await getInstanceFrameObj(imagePathObj); let instanceTotalFrameNumber = _.get(instanceFramesObj, "00280008.Value.0", 1); let windowCenter = _.get(instanceFramesObj, "00281050.Value.0", ""); let windowWidth = _.get(instanceFramesObj, "00281051.Value.0", ""); @@ -206,7 +128,7 @@ class WadoUriService { if (getFrameImageStatus.status) { return { - imageSharp: sharp(jpegFile), + imageSize: await imageSizeOfPromise(jpegFile), magick: new Magick(jpegFile) }; } @@ -215,17 +137,21 @@ class WadoUriService { } async handleImageQuality(magick) { - renderedService.handleImageQuality({ - quality: _.get(this.request.query, "imageQuality", "") - }, magick); + this.renderedImageProcessParameterHandler.handleImageQuality(magick); } - async handleRegion(param, imageSharp, magick) { + /** + * + * @param {Object} param + * @param {string} param.region + * @param {{height: number, width: number}} imageSize + * @param {Magick} magick + */ + async handleRegion(param, imageSize, magick) { if (param.region) { let [xMin, yMin, xMax, yMax] = param.region.split(",").map(v => parseFloat(v)); - let imageMetadata = await imageSharp.metadata(); - let imageWidth = imageMetadata.width; - let imageHeight = imageMetadata.height; + let imageWidth = imageSize.width; + let imageHeight = imageSize.height; let extractLeft = imageWidth * xMin; let extractTop = imageHeight * yMin; let extractWidth = imageWidth * xMax - extractLeft; @@ -234,21 +160,28 @@ class WadoUriService { } } - async handleRowsAndColumns(param, imageSharp, magick) { - let imageMetadata = await imageSharp.metadata(); + /** + * + * @param {Object} param + * @param {string} param.rows + * @param {string} param.columns + * @param {{height: number, width: number}} imageSize + * @param {Magick} magick + */ + async handleRowsAndColumns(param, imageSize, magick) { let rows = Number(param.rows); let columns = Number(param.columns); if (param.rows && param.columns) { magick.resize(rows, columns); } else if (param.rows) { - magick.resize(rows, imageMetadata.height); + magick.resize(rows, imageSize.height); } else if (param.columns) { - magick.resize(imageMetadata.width, columns); + magick.resize(imageSize.width, columns); } } async handleImageICCProfile(magick) { - await renderedService.handleImageICCProfile(this.request.query, magick, this.request.query.objectUID); + await this.renderedImageProcessParameterHandler.handleImageICCProfile(magick, this.request.query.objectUID); } async auditBeginTransferring() { diff --git a/api/controller.class.js b/api/controller.class.js index 2028cfdb..e8a7253c 100644 --- a/api/controller.class.js +++ b/api/controller.class.js @@ -1,8 +1,9 @@ const { pipe } = require("../utils/pipe.js"); const { pluginGroup, LocalPlugin } = require("../plugins/plugin.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger.js"); class Controller { - + /** * * @param {import('express').Request} req @@ -15,32 +16,32 @@ class Controller { async preProcess() { let currentRouterPlugin = pluginGroup.findLocalPlugin(this.request.originalUrl, this.request.method) || new LocalPlugin(); - + try { - for(let preFn of currentRouterPlugin.preFns) { + for (let preFn of currentRouterPlugin.preFns) { if (this.response.headersSent) break; await preFn(this.request, this.response); } - - } catch(e) { + + } catch (e) { throw new Error(`pre process error in path "${this.request.originalUrl}", ${e.message}`); } - + } - async mainProcess() {} + async mainProcess() { } async postProcess() { - let currentRouterPlugin = pluginGroup.findLocalPlugin(this.request.url, this.request.method) || new LocalPlugin(); + let currentRouterPlugin = pluginGroup.findLocalPlugin(this.request.originalUrl, this.request.method) || new LocalPlugin(); try { - for(let postFn of currentRouterPlugin.postFns) { + for (let postFn of currentRouterPlugin.postFns) { await postFn(this.request, this.response); } - } catch(e) { + } catch (e) { throw new Error(`post process error in path "${this.request.originalUrl}", ${e.message}`); } } @@ -50,7 +51,7 @@ class Controller { if (this.response.headersSent) return; await this.mainProcess(); - + this.postProcess(); } @@ -60,7 +61,7 @@ class Controller { paramsToString() { let strArr = []; let keys = Object.keys(this.request.params); - for(let i = 0 ; i < keys.length; i++) { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; strArr.push(`${key}: ${this.request.params[key]}`); } @@ -70,7 +71,7 @@ class Controller { queryToString() { let strArr = []; let keys = Object.keys(this.request.query); - for(let i = 0 ; i < keys.length; i++) { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; strArr.push(`${key}: ${this.request.query[key]}`); } diff --git a/api/dicom-web/controller/MWL-RS/change-filtered-mwlItem-status.js b/api/dicom-web/controller/MWL-RS/change-filtered-mwlItem-status.js new file mode 100644 index 00000000..65cd9eda --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/change-filtered-mwlItem-status.js @@ -0,0 +1,40 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { ChangeFilteredMwlItemStatusService } = require("@api/dicom-web/controller/MWL-RS/service/change-filtered-mwlItem-status"); + +class ChangeFilteredMwlItemStatusController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "MWL-RS"); + } + + async mainProcess() { + try { + let changeFilteredMwlItemService = new ChangeFilteredMwlItemStatusService(this.request, this.response); + let changedMwlItemsCount = await changeFilteredMwlItemService.changeMwlItemsStatus(); + + return this.response + .set("Content-Type", "application/dicom+json") + .status(200) + .json({ + updatedCount: changedMwlItemsCount + }); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new ChangeFilteredMwlItemStatusController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/change-mwlItem-status.js b/api/dicom-web/controller/MWL-RS/change-mwlItem-status.js new file mode 100644 index 00000000..f0873724 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/change-mwlItem-status.js @@ -0,0 +1,38 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { ChangeMwlItemStatusService } = require("@api/dicom-web/controller/MWL-RS/service/change-mwlItem-status"); + +class ChangeMwlItemStatusController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "MWL-RS"); + } + + async mainProcess() { + try { + let changeMwlItemService = new ChangeMwlItemStatusService(this.request, this.response); + let changedMwlItems = await changeMwlItemService.changeMwlItemsStatus(); + + return this.response + .set("Content-Type", "application/dicom+json") + .status(200) + .json(changedMwlItems); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new ChangeMwlItemStatusController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/count-mwlItem.js b/api/dicom-web/controller/MWL-RS/count-mwlItem.js new file mode 100644 index 00000000..ab630370 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/count-mwlItem.js @@ -0,0 +1,39 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { GetMwlItemCountService } = require("@api/dicom-web/controller/MWL-RS/service/count-mwlItem.service"); + +class GetMwlItemCountController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "MWL-RS"); + } + + async mainProcess() { + try { + let getMwlItemCountService = new GetMwlItemCountService(this.request, this.response); + let count = await getMwlItemCountService.getMwlItemCount(); + return this.response + .set("Content-Type", "application/dicom+json") + .status(200) + .json({ + count + }); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new GetMwlItemCountController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/create-mwlItem.js b/api/dicom-web/controller/MWL-RS/create-mwlItem.js new file mode 100644 index 00000000..5a7e3ae3 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/create-mwlItem.js @@ -0,0 +1,37 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { CreateMwlItemService } = require("@root/api/dicom-web/controller/MWL-RS/service/create-mwlItem.service"); + +class CreateMwlItemController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "MWL-RS"); + } + + async mainProcess() { + try { + let createMwlItemService = new CreateMwlItemService(this.request, this.response); + let mwlItem = await createMwlItemService.create(); + return this.response + .set("Content-Type", "application/dicom+json") + .status(201) + .json(mwlItem); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new CreateMwlItemController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/delete-mwlItem.js b/api/dicom-web/controller/MWL-RS/delete-mwlItem.js new file mode 100644 index 00000000..d038ae12 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/delete-mwlItem.js @@ -0,0 +1,38 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { DeleteMwlItemService } = require("./service/delete-mwlItem.service"); + +class DeleteMwlItemCountController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "MWL-RS"); + } + + async mainProcess() { + try { + let deleteMwlItemService = new DeleteMwlItemService(this.request, this.response); + await deleteMwlItemService.deleteMwlItem(); + + return this.response + .set("Content-Type", "application/dicom+json") + .status(200) + .send(); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new DeleteMwlItemCountController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/get-mwlItem.js b/api/dicom-web/controller/MWL-RS/get-mwlItem.js new file mode 100644 index 00000000..4e78a132 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/get-mwlItem.js @@ -0,0 +1,40 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { GetMwlItemService } = require("@api/dicom-web/controller/MWL-RS/service/get-mwlItem.service"); + +class GetMwlItemController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "MWL-RS"); + } + + async mainProcess() { + try { + let mwlItems = await new GetMwlItemService(this.request, this.response).getMwlItems(); + + + if (mwlItems.length === 0) return this.response.status(204).end(); + + return this.response + .set("Content-Type", "application/dicom+json") + .status(200) + .json(mwlItems); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new GetMwlItemController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/service/change-filtered-mwlItem-status.js b/api/dicom-web/controller/MWL-RS/service/change-filtered-mwlItem-status.js new file mode 100644 index 00000000..d35b03ea --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/service/change-filtered-mwlItem-status.js @@ -0,0 +1,22 @@ +const _ = require("lodash"); +const { MwlItemModel } = require("@dbModels/mwlitems.model"); +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { convertRequestQueryToMongoQuery } = require("@models/mongodb/convertQuery"); +const { BaseQueryService } = require("@root/api/dicom-web/service/base-query.service"); + +class ChangeFilteredMwlItemStatusService extends BaseQueryService { + constructor(req, res) { + super(req, res); + } + + async changeMwlItemsStatus() { + let { status } = this.request.params; + + let updatedCount = await MwlItemModel.updateStatusByQuery(this.query, status); + + return updatedCount; + } +} + +module.exports.ChangeFilteredMwlItemStatusService = ChangeFilteredMwlItemStatusService; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/service/change-mwlItem-status.js b/api/dicom-web/controller/MWL-RS/service/change-mwlItem-status.js new file mode 100644 index 00000000..8cc13739 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/service/change-mwlItem-status.js @@ -0,0 +1,31 @@ +const _ = require("lodash"); +const { MwlItemModel } = require("@dbModels/mwlitems.model"); +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); + +class ChangeMwlItemStatusService { + constructor(req, res) { + /** @type {import("express").Request} */ + this.request = req; + /** @type {import("express").Response} */ + this.response = res; + } + + async changeMwlItemsStatus() { + let { status } = this.request.params; + let mwlItem = await this.getMwlItemByStudyUIDAndSpsID(); + if (!mwlItem) { + throw new DicomWebServiceError(DicomWebStatusCodes.NoSuchObjectInstance, "No such object instance", 404); + } + + await mwlItem.updateStatus(status); + + return mwlItem.toGeneralDicomJson(); + } + + async getMwlItemByStudyUIDAndSpsID() { + return await MwlItemModel.findOneByStudyInstanceUIDAndSpsID(this.request.params.studyUID, this.request.params.spsID); + } +} + +module.exports.ChangeMwlItemStatusService = ChangeMwlItemStatusService; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/service/count-mwlItem.service.js b/api/dicom-web/controller/MWL-RS/service/count-mwlItem.service.js new file mode 100644 index 00000000..23175b0c --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/service/count-mwlItem.service.js @@ -0,0 +1,14 @@ +const { MwlItemModel } = require("@dbModels/mwlitems.model"); +const { BaseQueryService } = require("@root/api/dicom-web/service/base-query.service"); + +class GetMwlItemCountService extends BaseQueryService { + constructor(req, res) { + super(req, res); + } + + async getMwlItemCount() { + return await MwlItemModel.getCount(this.query); + } +} + +module.exports.GetMwlItemCountService = GetMwlItemCountService; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/service/create-mwlItem.service.js b/api/dicom-web/controller/MWL-RS/service/create-mwlItem.service.js new file mode 100644 index 00000000..f9f505b3 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/service/create-mwlItem.service.js @@ -0,0 +1,126 @@ +const _ = require("lodash"); +const { MwlItemModel } = require("@dbModels/mwlitems.model"); +const { PatientModel } = require("@dbModels/patient.model"); +const { StudyModel } = require("@dbModels/study.model"); +const crypto = require('crypto'); +const moment = require("moment"); +const { UIDUtils } = require("@dcm4che/util/UIDUtils"); +const { + DicomWebServiceError, + DicomWebStatusCodes +} = require("@error/dicom-web-service"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); +const { v4: uuidV4 } = require("uuid"); +const shortHash = require("shorthash2"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); + +class CreateMwlItemService { + constructor(req, res) { + this.request = req; + this.response = res; + this.requestMwlItem = /** @type {Object} */(this.request.body); + this.requestMwlItemDicomJson = new BaseDicomJson(this.requestMwlItem[0]); + this.apiLogger = new ApiLogger(req, "Create Mwl Item Service"); + this.apiLogger.addTokenValue(); + } + + async create() { + + await this.checkPatientExist(); + + let mwlItem = this.requestMwlItem[0]; + let mwlDicomJson = new BaseDicomJson(mwlItem); + + let spsItem = new BaseDicomJson({ + [dictionary.keyword.ScheduledProcedureStepSequence]: { + ...mwlItem[dictionary.keyword.ScheduledProcedureStepSequence] + } + }); + + let spsStatusPath = `${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepStatus}`; + if (!spsItem.getValue(spsStatusPath)) { + spsItem.setValue(spsStatusPath, "SCHEDULED"); + } + + let spsIDPath = `${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepID}`; + if (!spsItem.getValue(spsIDPath)) { + let spsID = shortHash(uuidV4()); + spsItem.setValue(spsIDPath, `SPS-${spsID}`); + } + mwlItem[dictionary.keyword.ScheduledProcedureStepSequence] = { + ...mwlItem[dictionary.keyword.ScheduledProcedureStepSequence], + ...spsItem.dicomJson[dictionary.keyword.ScheduledProcedureStepSequence] + }; + + if (!mwlDicomJson.getValue(dictionary.keyword.RequestedProcedureID)) { + let rpID = shortHash(uuidV4()); + _.set(mwlItem, dictionary.keyword.RequestedProcedureID, { + vr: BaseDicomJson.getTagVrOfTag(dictionary.keyword.RequestedProcedureID), + Value: [ + `RP-${rpID}` + ] + }); + } + + if (!mwlDicomJson.getValue(dictionary.keyword.StudyInstanceUID)) { + let studyInstanceUID = await UIDUtils.createUID(); + _.set(mwlItem, dictionary.keyword.StudyInstanceUID, { + vr: BaseDicomJson.getTagVrOfTag(dictionary.keyword.StudyInstanceUID), + Value: [ + studyInstanceUID + ] + }); + } + + if (!mwlDicomJson.getValue(dictionary.keyword.AccessionNumber)) { + let accessionNumber = shortHash(uuidV4()); + _.set(mwlItem, dictionary.keyword.AccessionNumber, { + vr: BaseDicomJson.getTagVrOfTag(dictionary.keyword.AccessionNumber), + Value: [ + accessionNumber + ] + }); + } + + return await this.createOrUpdateMwl(mwlDicomJson); + } + + async checkPatientExist() { + let patientID = this.requestMwlItemDicomJson.getString("00100020"); + let patientCount = await PatientModel.getCountByPatientID(patientID); + if (patientCount <= 0) { + throw new DicomWebServiceError( + DicomWebStatusCodes.MissingAttribute, + `Patient[id=${patientID}] does not exists`, + 404 + ); + } + } + + /** + * + * @param {BaseDicomJson} mwlDicomJson + */ + async createOrUpdateMwl(mwlDicomJson) { + let studyInstanceUID = mwlDicomJson.getValue(dictionary.keyword.StudyInstanceUID); + let spsItem = new BaseDicomJson(mwlDicomJson.getValue(dictionary.keyword.ScheduledProcedureStepSequence)); + let spsID = spsItem.getValue(dictionary.keyword.ScheduledProcedureStepID); + let foundMwl = await MwlItemModel.findOneByStudyInstanceUIDAndSpsID(studyInstanceUID, spsID); + + if (!foundMwl) { + // create + let createdMwlItem = await MwlItemModel.createWithGeneralDicomJson(mwlDicomJson.dicomJson); + this.apiLogger.logger.info(`create mwl item: ${studyInstanceUID}`); + return createdMwlItem.toGeneralDicomJson(); + } else { + // update + let updatedMwlItem = await MwlItemModel.updateOneWithGeneralDicomJson(foundMwl, mwlDicomJson.dicomJson); + this.apiLogger.logger.info(`update mwl item: ${studyInstanceUID}`); + return updatedMwlItem.toGeneralDicomJson(); + } + } + +} + +module.exports.CreateMwlItemService = CreateMwlItemService; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/service/delete-mwlItem.service.js b/api/dicom-web/controller/MWL-RS/service/delete-mwlItem.service.js new file mode 100644 index 00000000..634300ff --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/service/delete-mwlItem.service.js @@ -0,0 +1,22 @@ +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { MwlItemModel } = require("@dbModels/mwlitems.model"); + +class DeleteMwlItemService { + constructor(req, res) { + /** @type { import("express").Request } */ + this.request = req; + /** @type { import("express").Response } */ + this.response = res; + } + + async deleteMwlItem() { + const { studyUID, spsID } = this.request.params; + let { deletedCount } = await MwlItemModel.deleteByStudyInstanceUIDAndSpsID(studyUID, spsID); + + if (!deletedCount) { + throw new DicomWebServiceError(DicomWebStatusCodes.NoSuchSOPInstance, "Modality Worklist Item not found.", 404); + } + } +} + +module.exports.DeleteMwlItemService = DeleteMwlItemService; \ No newline at end of file diff --git a/api/dicom-web/controller/MWL-RS/service/get-mwlItem.service.js b/api/dicom-web/controller/MWL-RS/service/get-mwlItem.service.js new file mode 100644 index 00000000..81446a19 --- /dev/null +++ b/api/dicom-web/controller/MWL-RS/service/get-mwlItem.service.js @@ -0,0 +1,25 @@ +const { MwlItemModel } = require("@dbModels/mwlitems.model"); +const { BaseQueryService } = require("@root/api/dicom-web/service/base-query.service"); +const { QueryMwlDicomJsonFactory } = require("@api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory"); + +class GetMwlItemService extends BaseQueryService { + constructor(req, res) { + super(req, res); + } + + async getMwlItems() { + let queryOptions = { + query: this.query, + skip: this.skip_, + limit: this.limit_, + includeFields: this.includeFields_ + }; + + let queryFactory = new QueryMwlDicomJsonFactory(queryOptions); + let docs = await queryFactory.getDicomJson(); + + return docs; + } +} + +module.exports.GetMwlItemService = GetMwlItemService; \ No newline at end of file diff --git a/api/dicom-web/controller/PAM-RS/create-patient.js b/api/dicom-web/controller/PAM-RS/create-patient.js new file mode 100644 index 00000000..b1e33e77 --- /dev/null +++ b/api/dicom-web/controller/PAM-RS/create-patient.js @@ -0,0 +1,33 @@ +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { CreatePatientService } = require("@api/dicom-web/controller/PAM-RS/service/create-patient.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class CreatePatientController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "PAM-RS"); + this.apiLogger.addTokenValue(); + } + async mainProcess() { + this.apiLogger.logger.info("Create Patient"); + + let createPatientService = new CreatePatientService(this.request, this.response); + try { + let createPatientID = await createPatientService.create(); + return this.response + .set("content-type", "application/dicom+json") + .status(201) + .json(createPatientID); + } catch(e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +module.exports = async function (req, res) { + let controller = new CreatePatientController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/PAM-RS/delete-patient.js b/api/dicom-web/controller/PAM-RS/delete-patient.js new file mode 100644 index 00000000..cc3bf3e5 --- /dev/null +++ b/api/dicom-web/controller/PAM-RS/delete-patient.js @@ -0,0 +1,33 @@ +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { DeletePatientService } = require("@api/dicom-web/controller/PAM-RS/service/delete-patient.service"); + +class DeletePatientController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "PAM-RS"); + this.apiLogger.addTokenValue(); + } + async mainProcess() { + this.apiLogger.logger.info(`Delete Patient: ${this.request.params.patientID}`); + + let deletePatientService = new DeletePatientService(this.request, this.response); + try { + await deletePatientService.delete(); + return this.response + .set("content-type", "application/dicom+json") + .status(200) + .send(); + } catch(e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +module.exports = async function (req, res) { + let controller = new DeletePatientController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/PAM-RS/service/create-patient.service.js b/api/dicom-web/controller/PAM-RS/service/create-patient.service.js new file mode 100644 index 00000000..1d4f5cb2 --- /dev/null +++ b/api/dicom-web/controller/PAM-RS/service/create-patient.service.js @@ -0,0 +1,24 @@ +const { PatientModel } = require("@dbModels/patient.model"); +const { set, get } = require("lodash"); + +class CreatePatientService { + /** + * + * @param {import("express").Request} req + * @param {import("express").Response} res + */ + constructor(req, res) { + this.request = req; + this.response = res; + } + + async create() { + let incomingPatient = this.request.body; + let patientID = get(incomingPatient, "00100020.Value.0"); + set(incomingPatient, "patientID", patientID); + let patient = await PatientModel.createOrUpdatePatient(patientID, incomingPatient); + return patient.toGeneralDicomJson(); + } +} + +module.exports.CreatePatientService = CreatePatientService; \ No newline at end of file diff --git a/api/dicom-web/controller/PAM-RS/service/delete-patient.service.js b/api/dicom-web/controller/PAM-RS/service/delete-patient.service.js new file mode 100644 index 00000000..b1eb680a --- /dev/null +++ b/api/dicom-web/controller/PAM-RS/service/delete-patient.service.js @@ -0,0 +1,27 @@ +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { PatientModel } = require("@dbModels/patient.model"); + +class DeletePatientService { + constructor(req, res) { + /** @type { import("express").Request } */ + this.request = req; + /** @type { import("express").Response } */ + this.response = res; + } + + async delete() { + let { patientID } = this.request.params; + let patient = await PatientModel.findOneByPatientID(patientID); + if (!patient) { + throw new DicomWebServiceError( + DicomWebStatusCodes.NoSuchObjectInstance, + `Patient "${this.request.params.patientID}" does not exist.`, + 404 + ); + } + + await patient.incrementDeleteStatus(); + } +} + +module.exports.DeletePatientService = DeletePatientService; \ No newline at end of file diff --git a/api/dicom-web/controller/PAM-RS/service/update-patient.service.js b/api/dicom-web/controller/PAM-RS/service/update-patient.service.js new file mode 100644 index 00000000..c117589d --- /dev/null +++ b/api/dicom-web/controller/PAM-RS/service/update-patient.service.js @@ -0,0 +1,27 @@ +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { PatientModel } = require("@dbModels/patient.model"); +const { set } = require("lodash"); + + +class UpdatePatientService { + constructor(req, res) { + /** @type { import("express").Request } */ + this.request = req; + /** @type { import("express").Response } */ + this.response = res; + this.incomingPatient = this.request.body; + this.#adjustIncomingPatient(); + } + + async update() { + let { patientID } = this.request.params; + return await PatientModel.createOrUpdatePatient(patientID, { ...this.incomingPatient }); + } + + #adjustIncomingPatient() { + set(this.incomingPatient, "00100020.Value", [this.request.params.patientID]); + set(this.incomingPatient, "patientID", this.request.params.patientID); + } +} + +module.exports.UpdatePatientService = UpdatePatientService; \ No newline at end of file diff --git a/api/dicom-web/controller/PAM-RS/update-patient.js b/api/dicom-web/controller/PAM-RS/update-patient.js new file mode 100644 index 00000000..8f705377 --- /dev/null +++ b/api/dicom-web/controller/PAM-RS/update-patient.js @@ -0,0 +1,33 @@ +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { UpdatePatientService } = require("@api/dicom-web/controller/PAM-RS/service/update-patient.service"); + +class UpdatePatientController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(this.request, "PAM-RS"); + this.apiLogger.addTokenValue(); + } + async mainProcess() { + this.apiLogger.logger.info(`Update Patient: ${this.request.params.patientID}`); + + let updatePatientService = new UpdatePatientService(this.request, this.response); + try { + let updatedPatient = await updatePatientService.update(); + return this.response + .set("content-type", "application/dicom+json") + .status(200) + .json(await updatedPatient.toGeneralDicomJson()); + } catch(e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +module.exports = async function (req, res) { + let controller = new UpdatePatientController(req, res); + + await controller.doPipeline(); +}; \ No newline at end of file diff --git a/api/dicom-web/controller/QIDO-RS/allPatient.js b/api/dicom-web/controller/QIDO-RS/allPatient.js deleted file mode 100644 index cec7a0c0..00000000 --- a/api/dicom-web/controller/QIDO-RS/allPatient.js +++ /dev/null @@ -1,46 +0,0 @@ -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QueryAllPatientsController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info("Query all patients"); - - try { - let qidoRsService = new QidoRsService(this.request, this.response, "patient"); - - await qidoRsService.getAndResponseDicomJson(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} - -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QueryAllPatientsController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/QIDO-RS/base.controller.js b/api/dicom-web/controller/QIDO-RS/base.controller.js new file mode 100644 index 00000000..cb89dfe9 --- /dev/null +++ b/api/dicom-web/controller/QIDO-RS/base.controller.js @@ -0,0 +1,30 @@ +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { QidoRsService } = require("@api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseQueryController extends Controller { + constructor(req, res) { + super(req, res); + } + + logAction() { + throw new Error("Not Implemented"); + } + + async mainProcess() { + try { + let qidoRsService = new QidoRsService(this.request, this.response, this.request.dicomLevel); + let foundDicomJson = await qidoRsService.getDicomJson(); + if (foundDicomJson.length === 0 ) { + return this.response.status(204).send(); + } + return this.response.status(200).set("Content-Type", "application/dicom+json").json(foundDicomJson); + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +module.exports.BaseQueryController = BaseQueryController; \ No newline at end of file diff --git a/api/dicom-web/controller/QIDO-RS/queryAllInstances.js b/api/dicom-web/controller/QIDO-RS/queryAllInstances.js deleted file mode 100644 index 7b09c869..00000000 --- a/api/dicom-web/controller/QIDO-RS/queryAllInstances.js +++ /dev/null @@ -1,46 +0,0 @@ -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QueryAllInstancesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info("Query all instances"); - - try { - let qidoRsService = new QidoRsService(this.request, this.response, "instance"); - - await qidoRsService.getAndResponseDicomJson(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} - -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QueryAllInstancesController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/QIDO-RS/queryAllSeries.js b/api/dicom-web/controller/QIDO-RS/queryAllSeries.js deleted file mode 100644 index fef3e682..00000000 --- a/api/dicom-web/controller/QIDO-RS/queryAllSeries.js +++ /dev/null @@ -1,47 +0,0 @@ -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QueryAllSeriesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info("Query all series"); - - try { - - let qidoRsService = new QidoRsService(this.request, this.response, "series"); - - await qidoRsService.getAndResponseDicomJson(); - - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QueryAllSeriesController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/QIDO-RS/queryAllStudies.js b/api/dicom-web/controller/QIDO-RS/queryAllStudies.js deleted file mode 100644 index 78eed23c..00000000 --- a/api/dicom-web/controller/QIDO-RS/queryAllStudies.js +++ /dev/null @@ -1,49 +0,0 @@ -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QueryAllStudiesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info(`Query All Studies`); - - try { - - let qidoRsService = new QidoRsService(this.request, this.response, "study"); - - await qidoRsService.getAndResponseDicomJson(); - - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} - -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QueryAllStudiesController(req, res); - - await controller.doPipeline(); -}; - diff --git a/api/dicom-web/controller/QIDO-RS/queryStudies-Instances.js b/api/dicom-web/controller/QIDO-RS/queryStudies-Instances.js deleted file mode 100644 index d9d427e7..00000000 --- a/api/dicom-web/controller/QIDO-RS/queryStudies-Instances.js +++ /dev/null @@ -1,47 +0,0 @@ -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QueryInstancesOfStudiesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info(`Query instances in study, Study UID: ${this.request.params.studyUID}`); - - try { - - let qidoRsService = new QidoRsService(this.request, this.response, "instance"); - - await qidoRsService.getAndResponseDicomJson(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} - -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QueryInstancesOfStudiesController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/QIDO-RS/queryStudies-Series-Instance.js b/api/dicom-web/controller/QIDO-RS/queryStudies-Series-Instance.js deleted file mode 100644 index 1044f701..00000000 --- a/api/dicom-web/controller/QIDO-RS/queryStudies-Series-Instance.js +++ /dev/null @@ -1,49 +0,0 @@ -const _ = require("lodash"); -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QueryInstancesOfSeriesOfStudiesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info(`Query instance Level, Study UID: ${this.request.params.studyUID}, Series UID: ${this.request.params.seriesUID}`); - - try { - - let qidoRsService = new QidoRsService(this.request, this.response, "instance"); - - await qidoRsService.getAndResponseDicomJson(); - - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} - -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QueryInstancesOfSeriesOfStudiesController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/QIDO-RS/queryStudies-Series.js b/api/dicom-web/controller/QIDO-RS/queryStudies-Series.js deleted file mode 100644 index b375a7a8..00000000 --- a/api/dicom-web/controller/QIDO-RS/queryStudies-Series.js +++ /dev/null @@ -1,47 +0,0 @@ -const { - QidoRsService -} = require("./service/QIDO-RS.service"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); - -class QuerySeriesOfStudiesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "QIDO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info(`Query series Level, Study UID: ${this.request.params.studyUID}`); - - try { - - let qidoRsService = new QidoRsService(this.request, this.response, "series"); - - await qidoRsService.getAndResponseDicomJson(); - - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } -} -/** - * - * @param {import('express').Request} req - * @param {import('express').Response} res - */ -module.exports = async function (req, res) { - let controller = new QuerySeriesOfStudiesController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service.js b/api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service.js index 761d202e..caeb450f 100644 --- a/api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service.js +++ b/api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service.js @@ -1,31 +1,30 @@ -const urlObj = require("url"); -const mongoose = require("mongoose"); const _ = require("lodash"); -const { mongoDateQuery, timeQuery } = require("../../../../../models/mongodb/service"); -const { dictionary } = require("../../../../../models/DICOM/dicom-tags-dic"); -const { - tagsOfRequiredMatching -} = require("../../../../../models/DICOM/dicom-tags-mapping"); -const { logger } = require("../../../../../utils/logs/log"); -const { raccoonConfig } = require("../../../../../config-class"); const { DicomWebService } = require("../../../service/dicom-web.service"); -const dicomWebApiPath = raccoonConfig.dicomWebConfig.apiPath; -const dicomModel = require("../../../../../models/mongodb/models/dicom"); -const patientModel = require("../../../../../models/mongodb/models/patient"); -const { - DicomWebServiceError, - DicomWebStatusCodes -} = require("@error/dicom-web-service"); const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); +const { + QueryPatientDicomJsonFactory, + QueryStudyDicomJsonFactory, + QuerySeriesDicomJsonFactory, + QueryInstanceDicomJsonFactory +} = require("@api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory"); +const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); + +const HierarchyQueryDicomJsonFactory = Object.freeze({ + patient: QueryPatientDicomJsonFactory, + study: QueryStudyDicomJsonFactory, + series: QuerySeriesDicomJsonFactory, + instance: QueryInstanceDicomJsonFactory +}); class QidoRsService { /** * * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {import('express').Response} res + * @param { "patient" | "study" | "series" | "instance" } level */ constructor(req, res, level="instance") { this.request = req; @@ -57,6 +56,10 @@ class QidoRsService { delete this.request.query["includefield"]; + /** @private */ + this.isRecycle = Boolean(this.request.query?.isRecycle); + delete this.request.query?.isRecycle; + this.initQuery_(); } @@ -71,10 +74,11 @@ class QidoRsService { if (!query[queryKey]) delete query[queryKey]; } - this.query = convertAllQueryToDICOMTag(query); + this.query = convertAllQueryToDicomTag(query); } - async getAndResponseDicomJson() { + + async getDicomJson() { try { let queryAudit = new AuditManager( EventType.QUERY, @@ -83,14 +87,14 @@ class QidoRsService { DicomWebService.getServerAddress(), DicomWebService.getServerHostname() ); let dicomWebService = new DicomWebService(this.request, this.response); - let queryOptions = { query: this.query, skip: this.skip_, limit: this.limit_, includeFields: this.includeFields_, retrieveBaseUrl: `${dicomWebService.getBasicURL()}/studies`, - requestParams: this.request.params + requestParams: this.request.params, + isRecycle: this.isRecycle }; queryAudit.onQuery( @@ -98,22 +102,16 @@ class QidoRsService { JSON.stringify({...queryOptions.requestParams,...queryOptions.query}), "UTF-8" ); - let qidoDicomJsonFactory = new QidoDicomJsonFactory(queryOptions, this.level); + let dicomJsonFactory = new HierarchyQueryDicomJsonFactory[this.level](queryOptions); - let dicomJson = await qidoDicomJsonFactory.getDicomJson(); + let dicomJson = await dicomJsonFactory.getDicomJson(); let dicomJsonLength = _.get(dicomJson, "length", 0); if (dicomJsonLength > 0) { this.auditInstancesAccessed(dicomJson); - this.response.writeHead(200, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify(dicomJson)); - } else { - this.response.writeHead(204); - this.response.end(); - } - + return dicomJson; + } + return []; } catch(e) { throw e; } @@ -136,324 +134,4 @@ class QidoRsService { } -class QidoDicomJsonFactory { - - /** - * - * @param {import("../../../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @param {string} level - */ - constructor(queryOptions, level="instance") { - this.level = level; - - this.getDicomJsonByLevel = { - "patient": async() => { - return await getPatientDicomJson(queryOptions); - }, - "study": async () => { - return await getStudyDicomJson(queryOptions); - }, - "series": async () => { - return await getSeriesDicomJson(queryOptions); - }, - "instance": async () => { - return await getInstanceDicomJson(queryOptions); - } - }; - } - - async getDicomJson() { - return await this.getDicomJsonByLevel[this.level](); - } -} - -/** - * Convert All of name(tags, keyword) of queries to tags number - * @param {Object} iParam The request query. - * @returns - */ -function convertAllQueryToDICOMTag(iParam) { - let keys = Object.keys(iParam); - let newQS = {}; - for (let i = 0; i < keys.length; i++) { - let keyName = keys[i]; - let keyNameSplit = keyName.split("."); - let newKeyNames = []; - for (let x = 0; x < keyNameSplit.length; x++) { - if (dictionary.keyword[keyNameSplit[x]]) { - newKeyNames.push(dictionary.keyword[keyNameSplit[x]]); - } else if (dictionary.tag[keyNameSplit[x]]) { - newKeyNames.push(keyNameSplit[x]); - } - } - let retKeyName; - if (newKeyNames.length === 0) { - throw new DicomWebServiceError( - DicomWebStatusCodes.InvalidArgumentValue, - `Invalid request query: ${keyNameSplit}`, - 400 - ); - } else if (newKeyNames.length >= 2) { - retKeyName = newKeyNames.map(v => v + ".Value").join("."); - } else { - newKeyNames.push("Value"); - retKeyName = newKeyNames.join("."); - } - - newQS[retKeyName] = iParam[keyName]; - } - return newQS; -} -//#endregion - -function checkIsOr(value, keyName) { - if (_.isObject(value) && _.get(value[keyName], "$or")) { - return true; - } - return false; -} - -/** - * convert value that contains comma to $or query of MongoDB - * @param {string} iKey - * @param {string} iValue - */ -function commaValue(iKey, iValue) { - let $or = []; - iValue = iValue.split(","); - for (let i = 0; i < iValue.length; i++) { - let obj = {}; - obj[iKey] = iValue[i]; - $or.push(obj); - } - return $or; -} - -/** - * - * @param {string} value - * @returns - */ -function getWildCardQuery(value) { - let wildCardIndex = value.indexOf("*"); - let questionIndex = value.indexOf("?"); - - if (wildCardIndex >= 0 || questionIndex >= 0) { - value = value.replace(/\*/gm, ".*"); - value = value.replace(/\?/gm, "."); - value = value.replace(/\^/gm, "\\^"); - value = "^" + value; - return new RegExp(value, "gm"); - } - - return value; -} - -/** - * convert all request query object to to $or query and push to $and query - * @param {Object} iQuery - * @returns - */ -async function convertRequestQueryToMongoQuery(iQuery) { - let queryKey = Object.keys(iQuery); - let mongoQs = { - $match: { - $and: [] - } - }; - for (let i = 0; i < queryKey.length; i++) { - let mongoOrs = { - $or: [] - }; - let nowKey = queryKey[i]; - let value = commaValue(nowKey, iQuery[nowKey]); - for (let x = 0; x < value.length; x++) { - let nowValue = value[x][nowKey]; - value[x][nowKey] = getWildCardQuery(nowValue); - - try { - let keySplit = nowKey.split("."); - let tag = keySplit[keySplit.length - 2]; - let vrOfTag = dictionary.tagVR[tag]; - await vrQueryLookup[vrOfTag.vr](value[x], nowKey); - } catch (e) { - if (!(e instanceof TypeError)) console.error(e); - } - - if (checkIsOr(value[x], nowKey)) { - mongoOrs.$or.push(..._.get(value[x][nowKey], "$or")); - } else { - mongoOrs.$or.push(value[x]); - } - } - mongoQs.$match.$and.push(mongoOrs); - } - return mongoQs.$match.$and.length == 0 - ? { - $match: {} - } - : mongoQs; -} - -/** - * - * @param {any[]} arr - * @returns - */ -function sortArrayObjByFieldKey(arr) { - return arr.map(v => sortObjByFieldKey(v)); -} - -function sortObjByFieldKey(obj) { - return _(obj).toPairs().sortBy(0).fromPairs().value(); -} - -const vrQueryLookup = { - DA: async (value, tag) => { - let q = await mongoDateQuery(value, tag, false); - }, - DT: async (value, tag) => { - let q = await mongoDateQuery(value, tag, false, "YYYYMMDDhhmmss.SSSSSSZZ"); - }, - PN: async (value, tag) => { - let queryValue = _.cloneDeep(value[tag]); - value[tag] = { - $or : [ - { - [`${tag}.Alphabetic`] : queryValue - }, - { - [`${tag}.familyName`] : queryValue - }, - { - [`${tag}.givenName`] : queryValue - } , - { - [`${tag}.middleName`] : queryValue - } , - { - [`${tag}.prefix`] : queryValue - }, - { - [`${tag}.suffix`] : queryValue - } - ]}; - }, - TM: async (value, tag) => { - value[tag] = timeQuery(value, tag); - } -}; - -/** - * - * @param {import("../../../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -async function getPatientDicomJson(queryOptions) { - try { - let mongoQuery = await convertRequestQueryToMongoQuery(queryOptions.query); - - let query = { - ...queryOptions.requestParams, - ...mongoQuery.$match - }; - logger.info(`[QIDO-RS] [Query for MongoDB: ${JSON.stringify(query)}]`); - - queryOptions.query = { ...query }; - let docs = await patientModel.getDicomJson(queryOptions); - - let sortedInstanceDicomJson = sortArrayObjByFieldKey(docs); - - return sortedInstanceDicomJson; - } catch (e) { - console.error("get Series DICOM error", e); - throw e; - } -} - -/** - * - * @param {import("../../../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -async function getStudyDicomJson(queryOptions) { - logger.info(`[QIDO-RS] [Query Study Level]`); - - try { - let query = await convertRequestQueryToMongoQuery(queryOptions.query); - queryOptions.query = { - ...queryOptions.requestParams, - ...query.$match - }; - - logger.info(`[QIDO-RS] [Query for MongoDB: ${JSON.stringify(queryOptions.query)}]`); - - let docs = await mongoose.model("dicomStudy").getDicomJson(queryOptions); - - let sortedTagsStudyDicomJson = sortArrayObjByFieldKey(docs); - - return sortedTagsStudyDicomJson; - } catch (e) { - console.error("get Study DICOM error", e); - throw e; - } -} - -/** - * - * @param {import("../../../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -async function getSeriesDicomJson(queryOptions) { - try { - let mongoQuery = await convertRequestQueryToMongoQuery(queryOptions.query); - - let query = { - ...queryOptions.requestParams, - ...mongoQuery.$match - }; - logger.info(`[QIDO-RS] [Query for MongoDB: ${JSON.stringify(query)}]`); - - queryOptions.query = { ...query }; - let docs = await mongoose.model("dicomSeries").getDicomJson(queryOptions); - - let sortedTagsSeriesDicomJson = sortArrayObjByFieldKey(docs); - - return sortedTagsSeriesDicomJson; - } catch (e) { - console.error("get Series DICOM error", e); - throw e; - } -} - -/** - * - * @param {import("../../../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -async function getInstanceDicomJson(queryOptions) { - try { - let mongoQuery = await convertRequestQueryToMongoQuery(queryOptions.query); - - let query = { - ...queryOptions.requestParams, - ...mongoQuery.$match - }; - logger.info(`[QIDO-RS] [Query for MongoDB: ${JSON.stringify(query)}]`); - - queryOptions.query = { ...query }; - let docs = await dicomModel.getDicomJson(queryOptions); - - let sortedInstanceDicomJson = sortArrayObjByFieldKey(docs); - - return sortedInstanceDicomJson; - } catch (e) { - console.error("get Series DICOM error", e); - throw e; - } -} - module.exports.QidoRsService = QidoRsService; -module.exports.QidoDicomJsonFactory = QidoDicomJsonFactory; -module.exports.convertAllQueryToDICOMTag = convertAllQueryToDICOMTag; -module.exports.convertRequestQueryToMongoQuery = convertRequestQueryToMongoQuery; diff --git a/api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory.js b/api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory.js new file mode 100644 index 00000000..5f8da6ec --- /dev/null +++ b/api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory.js @@ -0,0 +1,100 @@ +const _ = require("lodash"); +const { PatientModel } = require("@dbModels/patient.model"); +const { StudyModel } = require("@dbModels/study.model"); +const { SeriesModel } = require("@dbModels/series.model"); +const { InstanceModel } = require("@dbModels/instance.model"); +const { WorkItemModel } = require("@dbModels/workitems.model"); +const { convertRequestQueryToMongoQuery } = require("@models/mongodb/convertQuery"); +const { MwlItemModel } = require("@models/mongodb/models/mwlitems.model"); + +/** + * + * @param {any[]} arr + * @returns + */ +function sortArrayObjByFieldKey(arr) { + return arr.map(v => sortObjByFieldKey(v)); +} + +function sortObjByFieldKey(obj) { + return _(obj).toPairs().sortBy(0).fromPairs().value(); +} + + +class QueryDicomJsonFactory { + constructor(queryOptions) { + this.queryOptions = queryOptions; + this.model = PatientModel; + } + + async getProcessedQueryOptions() { + let mongoQuery = await convertRequestQueryToMongoQuery(this.queryOptions.query); + + let query = { + ...this.queryOptions.requestParams, + ...mongoQuery.$match + }; + + this.queryOptions.query = { ...query }; + return this.queryOptions; + } + + async getDicomJson() { + let processedQueryOptions = await this.getProcessedQueryOptions(); + let docs = await this.model.getDicomJson(processedQueryOptions); + + let sortedTagsSeriesDicomJson = sortArrayObjByFieldKey(docs); + + return sortedTagsSeriesDicomJson; + } +} + +class QueryPatientDicomJsonFactory extends QueryDicomJsonFactory { + constructor(queryOptions) { + super(queryOptions); + this.model = PatientModel; + } +} + +class QueryStudyDicomJsonFactory extends QueryDicomJsonFactory { + constructor(queryOptions) { + super(queryOptions); + this.model = StudyModel; + } +} + +class QuerySeriesDicomJsonFactory extends QueryDicomJsonFactory { + constructor(queryOptions) { + super(queryOptions); + this.model = SeriesModel; + } +} + +class QueryInstanceDicomJsonFactory extends QueryDicomJsonFactory { + constructor(queryOptions) { + super(queryOptions); + this.model = InstanceModel; + } +} + +class QueryUpsDicomJsonFactory extends QueryDicomJsonFactory { + constructor(queryOptions) { + super(queryOptions); + this.model = WorkItemModel; + } +} + +class QueryMwlDicomJsonFactory extends QueryDicomJsonFactory { + constructor(queryOptions) { + super(queryOptions); + this.model = MwlItemModel; + } +} + +module.exports.QueryDicomJsonFactory = QueryDicomJsonFactory; +module.exports.QueryPatientDicomJsonFactory = QueryPatientDicomJsonFactory; +module.exports.QueryStudyDicomJsonFactory = QueryStudyDicomJsonFactory; +module.exports.QuerySeriesDicomJsonFactory = QuerySeriesDicomJsonFactory; +module.exports.QueryInstanceDicomJsonFactory = QueryInstanceDicomJsonFactory; +module.exports.QueryUpsDicomJsonFactory = QueryUpsDicomJsonFactory; +module.exports.QueryMwlDicomJsonFactory = QueryMwlDicomJsonFactory; diff --git a/api/dicom-web/controller/STOW-RS/service/dicom-jpeg-generator.js b/api/dicom-web/controller/STOW-RS/service/dicom-jpeg-generator.js index 95fd4c5c..dff2b8b7 100644 --- a/api/dicom-web/controller/STOW-RS/service/dicom-jpeg-generator.js +++ b/api/dicom-web/controller/STOW-RS/service/dicom-jpeg-generator.js @@ -1,9 +1,9 @@ const fs = require("fs"); -const { Dcm2JpgExecutor } = require("../../../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor"); -const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("../../../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions"); -const notImageSOPClass = require("../../../../../models/DICOM/dicomWEB/notImageSOPClass"); -const { logger } = require("../../../../../utils/logs/log"); -const dicomToJpegTask = require("../../../../../models/mongodb/models/dicomToJpegTask"); +const { Dcm2JpgExecutor } = require("@models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor"); +const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("@models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions"); +const notImageSOPClass = require("@models/DICOM/dicomWEB/notImageSOPClass"); +const { logger } = require("@root/utils/logs/log"); +const { DicomToJpegTaskModel } = require("@dbModels/dicomToJpegTask.model.js"); const colorette = require("colorette"); /** @@ -98,7 +98,7 @@ class DicomJpegGenerator { let startTaskObj = { studyUID: this.dicomJsonModel.uidObj.studyUID, seriesUID: this.dicomJsonModel.uidObj.seriesUID, - instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID, + instanceUID: this.dicomJsonModel.uidObj.instanceUID, status: false, message: "processing", taskTime: new Date(), @@ -106,7 +106,7 @@ class DicomJpegGenerator { fileSize: `${(fs.statSync(this.dicomInstanceFilename).size / 1024 / 1024).toFixed(3)}MB` }; - await dicomToJpegTask.insertOrUpdate(startTaskObj); + await DicomToJpegTaskModel.insertOrUpdate(startTaskObj); } /** @@ -116,12 +116,12 @@ class DicomJpegGenerator { let endTaskObj = { studyUID: this.dicomJsonModel.uidObj.studyUID, seriesUID: this.dicomJsonModel.uidObj.seriesUID, - instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID, + instanceUID: this.dicomJsonModel.uidObj.instanceUID, status: true, message: "generated", finishedTime: new Date() }; - await dicomToJpegTask.insertOrUpdate(endTaskObj); + await DicomToJpegTaskModel.insertOrUpdate(endTaskObj); } /** @@ -132,12 +132,12 @@ class DicomJpegGenerator { let errorTaskObj = { studyUID: this.dicomJsonModel.uidObj.studyUID, seriesUID: this.dicomJsonModel.uidObj.seriesUID, - instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID, + instanceUID: this.dicomJsonModel.uidObj.instanceUID, status: false, message: message, finishedTime: new Date() }; - await dicomToJpegTask.insertOrUpdate(errorTaskObj); + await DicomToJpegTaskModel.insertOrUpdate(errorTaskObj); } } diff --git a/api/dicom-web/controller/STOW-RS/service/request-multipart-parser.js b/api/dicom-web/controller/STOW-RS/service/request-multipart-parser.js index c10c8af3..b8b94d04 100644 --- a/api/dicom-web/controller/STOW-RS/service/request-multipart-parser.js +++ b/api/dicom-web/controller/STOW-RS/service/request-multipart-parser.js @@ -12,7 +12,7 @@ class StowRsRequestMultipartParser { /** * - * @return {Promise} + * @return {Promise} */ async parse() { return new Promise((resolve, reject) => { diff --git a/api/dicom-web/controller/STOW-RS/service/stow-rs.service.js b/api/dicom-web/controller/STOW-RS/service/stow-rs.service.js index 36bac7a0..83bbc104 100644 --- a/api/dicom-web/controller/STOW-RS/service/stow-rs.service.js +++ b/api/dicom-web/controller/STOW-RS/service/stow-rs.service.js @@ -4,25 +4,15 @@ const { DicomJsonParser } = require("../../../../../models/DICOM/dicom-json-pars const { DicomJsonModel, DicomJsonBinaryDataModel -} = require("../../../../../models/DICOM/dicom-json-model"); +} = require("@dicom-json-model"); const { DicomFileSaver } = require("./dicom-file-saver"); -const { DicomFhirService } = require("./dicom-fhir.service"); const { DicomJpegGenerator } = require("./dicom-jpeg-generator"); const { logger } = require("../../../../../utils/logs/log"); - -const { raccoonConfig } = require("../../../../../config-class"); const { DicomWebService } = require("../../../service/dicom-web.service"); const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); -const { - apiPath: DICOM_WEB_API_PATH -} = raccoonConfig.dicomWebConfig; - -const { - isSyncToFhir -} = raccoonConfig.fhirConfig; const StowRsFailureCode = { "GENERAL_FAILURE": "272", @@ -34,11 +24,19 @@ const StowRsFailureCode = { class StowRsService { /** * @param {import('express').Request} req + * @param {import('express').Response} res * @param {import('formidable').File[]} uploadFiles */ - constructor(req, uploadFiles) { + constructor(req, res, uploadFiles) { this.request = req; + this.response = res; + + this.response.locals = { + "storeInfos": [] + }; + this.uploadFiles = uploadFiles; + this.responseMessage = { "00081190": { //Study retrieve URL @@ -90,14 +88,18 @@ class StowRsService { let storeInstanceResult = await this.storeInstance(currentFile); dicomJsonModel = storeInstanceResult.dicomJsonModel; dicomFileSaveInfo = storeInstanceResult.dicomFileSaveInfo; - } catch(e) { + this.response.locals.storeInfos.push({ + dicomFileSaveInfo, + dicomJsonModel + }); + } catch (e) { // log transferred failure let auditManager = new AuditManager( EventType.STORE_CREATE, EventOutcomeIndicator.MajorFailure, DicomWebService.getRemoteAddress(this.request), DicomWebService.getRemoteHostname(this.request), DicomWebService.getServerAddress(), DicomWebService.getServerHostname() ); - + await auditManager.onDicomInstancesTransferred( dicomJsonModel ? [dicomJsonModel.uidObj.studyUID] : "Unknown" ); @@ -105,14 +107,6 @@ class StowRsService { throw e; } - - //sync DICOM to FHIR - if (isSyncToFhir) { - let dicomFhirService = new DicomFhirService(this.request, dicomJsonModel); - await dicomFhirService.initDicomFhirConverter(); - await dicomFhirService.postDicomToFhirServerAndStoreLog(); - } - //generate JPEG let dicomJpegGenerator = new DicomJpegGenerator(dicomJsonModel, dicomFileSaveInfo.instancePath); dicomJpegGenerator.generateAllFrames(); @@ -170,7 +164,7 @@ class StowRsService { this.responseMessage["00081190"].Value.push(retrieveUrlObj.study); this.responseMessage["00081190"].Value = _.uniq(this.responseMessage["00081190"].Value); - let sopSeq = this.getSOPSeq(dicomJsonModel.uidObj.sopClass, dicomJsonModel.uidObj.sopInstanceUID); + let sopSeq = this.getSOPSeq(dicomJsonModel.uidObj.sopClass, dicomJsonModel.uidObj.instanceUID); _.set(sopSeq, "00081190.vr", "UT"); _.set(sopSeq, "00081190.Value", [retrieveUrlObj.instance]); this.responseMessage["00081199"]["Value"].push(sopSeq); @@ -195,7 +189,7 @@ class StowRsService { isSameStudyID_(uidObj, storeMessage) { let reqStudyId = this.request.params.studyID; let dataStudyId = uidObj.studyUID; - let sopSeq = this.getSOPSeq(uidObj.sopClass, uidObj.sopInstanceUID); + let sopSeq = this.getSOPSeq(uidObj.sopClass, uidObj.instanceUID); let result = true; if (reqStudyId) { @@ -262,7 +256,7 @@ class StowRsService { return { study: `${url}/${uidObj.studyUID}`, series: `${url}/${uidObj.studyUID}/series/${uidObj.seriesUID}`, - instance: `${url}/${uidObj.studyUID}/series/${uidObj.seriesUID}/instances/${uidObj.sopInstanceUID}` + instance: `${url}/${uidObj.studyUID}/series/${uidObj.seriesUID}/instances/${uidObj.instanceUID}` }; } diff --git a/api/dicom-web/controller/STOW-RS/storeInstance.js b/api/dicom-web/controller/STOW-RS/storeInstance.js index 9a47cc7c..31fb4597 100644 --- a/api/dicom-web/controller/STOW-RS/storeInstance.js +++ b/api/dicom-web/controller/STOW-RS/storeInstance.js @@ -4,6 +4,7 @@ const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { StowRsRequestMultipartParser } = require("./service/request-multipart-parser"); const { StowRsService } = require("./service/stow-rs.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class StoreInstanceController extends Controller { constructor(req, res) { @@ -14,15 +15,15 @@ class StoreInstanceController extends Controller { let startSTOWTime = performance.now(); let retCode; let storeMessage; - let apiLogger = new ApiLogger(this.request, "STOW-RS"); - apiLogger.addTokenValue(); + this.apiLogger = new ApiLogger(this.request, "STOW-RS"); + this.apiLogger.addTokenValue(); try { let requestMultipartParser = new StowRsRequestMultipartParser(this.request); let multipartParseResult = await requestMultipartParser.parse(); if (multipartParseResult.status) { - let stowRsService = new StowRsService(this.request, multipartParseResult.multipart.files); + let stowRsService = new StowRsService(this.request, this.response, multipartParseResult.multipart.files); let storeInstancesResult = await stowRsService.storeInstances(); retCode = storeInstancesResult.code; @@ -30,7 +31,7 @@ class StoreInstanceController extends Controller { } let endSTOWTime = performance.now(); let elapsedTime = (endSTOWTime - startSTOWTime).toFixed(3); - apiLogger.logger.info(`Finished STOW-RS, elapsed time: ${elapsedTime} ms`); + this.apiLogger.logger.info(`Finished STOW-RS, elapsed time: ${elapsedTime} ms`); this.response.writeHead(retCode, { "Content-Type": "application/dicom" @@ -38,15 +39,8 @@ class StoreInstanceController extends Controller { return this.response.end(JSON.stringify(storeMessage)); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - let errorMessage = - errorResponseMessage.getInternalServerErrorMessage(errorStr); - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(errorMessage)); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/cancel.js b/api/dicom-web/controller/UPS-RS/cancel.js index a58ac60c..a0da82f9 100644 --- a/api/dicom-web/controller/UPS-RS/cancel.js +++ b/api/dicom-web/controller/UPS-RS/cancel.js @@ -6,21 +6,22 @@ const { CancelWorkItemService -} = require("./service/cancel.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/cancel.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class CancelWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`Cancel Work Item, params: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`Cancel Work Item, params: ${this.paramsToString()}`); try { let service = new CancelWorkItemService(this.request, this.response); @@ -31,23 +32,8 @@ class CancelWorkItemController extends Controller { .status(202) .end(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).json({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/change-workItem-state.js b/api/dicom-web/controller/UPS-RS/change-workItem-state.js index 17c14915..ab2a3faa 100644 --- a/api/dicom-web/controller/UPS-RS/change-workItem-state.js +++ b/api/dicom-web/controller/UPS-RS/change-workItem-state.js @@ -1,20 +1,21 @@ const { ChangeWorkItemStateService -} = require("./service/change-workItem-state.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/change-workItem-state.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class ChangeWorkItemStateController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`Update workItem, params: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`Update workItem, params: ${this.paramsToString()}`); try { let service = new ChangeWorkItemStateService(this.request, this.response); @@ -24,23 +25,8 @@ class ChangeWorkItemStateController extends Controller { .status(200) .end(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).json({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/create-workItems.js b/api/dicom-web/controller/UPS-RS/create-workItems.js index 41091426..3cbf429b 100644 --- a/api/dicom-web/controller/UPS-RS/create-workItems.js +++ b/api/dicom-web/controller/UPS-RS/create-workItems.js @@ -1,45 +1,31 @@ const _ = require("lodash"); const { CreateWorkItemService -} = require("./service/create-workItem.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/create-workItem.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class CreateWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info("Create workItem"); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info("Create workItem"); try { let workItemService = new CreateWorkItemService(this.request, this.response); let workItem = await workItemService.createUps(); - apiLogger.logger.info(`Create workItem ${workItem.upsInstanceUID} successful`); + this.apiLogger.logger.info(`Create workItem ${workItem.upsInstanceUID} successful`); return this.response.status(201).send(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).send({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/get-workItem.js b/api/dicom-web/controller/UPS-RS/get-workItem.js index 1ef3ead6..bf87c0ee 100644 --- a/api/dicom-web/controller/UPS-RS/get-workItem.js +++ b/api/dicom-web/controller/UPS-RS/get-workItem.js @@ -1,20 +1,21 @@ const _ = require("lodash"); const { GetWorkItemService -} = require("./service/get-workItem.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/get-workItem.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class GetWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`Get workItem, query: ${this.queryToString()}, param: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`Get workItem, query: ${this.queryToString()}, param: ${this.paramsToString()}`); try { let getWorkItemService = new GetWorkItemService(this.request, this.response); @@ -28,16 +29,8 @@ class GetWorkItemController extends Controller { return this.response.set("Content-Type", "application/dicom+json").status(200).json(workItems); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/service/base-workItem.service.js b/api/dicom-web/controller/UPS-RS/service/base-workItem.service.js index 8b5ddec3..fac31bbc 100644 --- a/api/dicom-web/controller/UPS-RS/service/base-workItem.service.js +++ b/api/dicom-web/controller/UPS-RS/service/base-workItem.service.js @@ -1,13 +1,13 @@ const _ = require("lodash"); const { WorkItemEvent } = require("./workItem-event"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); const { findWsArrayByAeTitle } = require("@root/websocket"); const { SUBSCRIPTION_STATE } = require("@models/DICOM/ups"); -const { convertRequestQueryToMongoQuery } = require("../../QIDO-RS/service/QIDO-RS.service"); -const globalSubscriptionModel = require("@models/mongodb/models/upsGlobalSubscription"); -const subscriptionModel = require("@models/mongodb/models/upsSubscription"); -const workItemModel = require("@models/mongodb/models/workItems"); +const { UpsGlobalSubscriptionModel } = require("@dbModels/upsGlobalSubscription"); +const { UpsSubscriptionModel } = require("@dbModels/upsSubscription"); +const { WorkItemModel } = require("@dbModels/workitems.model"); const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); class BaseWorkItemService { constructor(req, res) { @@ -17,8 +17,6 @@ class BaseWorkItemService { this.response = res; /** @type {WorkItemEvent[]} */ this.upsEvents = []; - /** @type {DicomJsonModel} */ - this.workItem = null; } addUpsEvent(type, upsInstanceUID, eventInformation, subscribers) { @@ -48,7 +46,7 @@ class BaseWorkItemService { /** * Use for getting event information - * @param {DicomJsonModel} workItem + * @param {BaseDicomJson} workItem */ stateReportOf(workItem) { let eventInformation = { @@ -84,9 +82,7 @@ class BaseWorkItemService { } async isAeTileSubscribed(aeTitle) { - let subscription = await subscriptionModel.findOne({ - aeTitle: aeTitle - }); + let subscription = await UpsSubscriptionModel.findOneByAeTitle(aeTitle); if (!subscription) return false; @@ -96,11 +92,11 @@ class BaseWorkItemService { } async getGlobalSubscriptionsCursor() { - return globalSubscriptionModel.find({}).cursor(); + return await UpsGlobalSubscriptionModel.getCursor({}); } /** - * @param {DicomJsonModel} workItem + * @param {any} workItem repository workItem */ async getHitGlobalSubscriptions(workItem) { let globalSubscriptionsCursor = await this.getGlobalSubscriptionsCursor(); @@ -110,13 +106,7 @@ class BaseWorkItemService { if (!globalSubscription.queryKeys) { hitGlobalSubscriptions.push(globalSubscription); } else { - let { $match } = await convertRequestQueryToMongoQuery(globalSubscription.queryKeys); - $match.$and.push({ - upsInstanceUID: workItem.dicomJson.upsInstanceUID - }); - let count = await workItemModel.countDocuments({ - ...$match - }); + let count = await WorkItemModel.getCountWithQueryAndUpsInstanceUID(globalSubscription.queryKeys, workItem.upsInstanceUID); if (count > 0) hitGlobalSubscriptions.push(globalSubscription); } @@ -125,10 +115,13 @@ class BaseWorkItemService { return hitGlobalSubscriptions; } + /** + * + * @param {any} workItem repository workItem + * @returns + */ async getHitSubscriptions(workItem) { - let hitSubscriptions = await subscriptionModel.find({ - workItems: workItem.dicomJson._id - }); + let hitSubscriptions = await UpsSubscriptionModel.findByWorkItem(workItem); return hitSubscriptions; } diff --git a/api/dicom-web/controller/UPS-RS/service/cancel.service.js b/api/dicom-web/controller/UPS-RS/service/cancel.service.js index 4391971c..5e6ed5ea 100644 --- a/api/dicom-web/controller/UPS-RS/service/cancel.service.js +++ b/api/dicom-web/controller/UPS-RS/service/cancel.service.js @@ -1,15 +1,14 @@ const _ = require("lodash"); -const workItemModel = require("@models/mongodb/models/workItems"); -const { DicomJsonModel, BaseDicomJson } = require("@models/DICOM/dicom-json-model"); -const globalSubscriptionModel = require("@models/mongodb/models/upsGlobalSubscription"); +const { BaseDicomJson } = require("@dicom-json-model"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); -const { BaseWorkItemService } = require("./base-workItem.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); const { dictionary } = require("@models/DICOM/dicom-tags-dic"); const { UPS_EVENT_TYPE } = require("./workItem-event"); const { raccoonConfig } = require("@root/config-class"); +const { WorkItemModel } = require("@dbModels/workitems.model"); class CancelWorkItemService extends BaseWorkItemService { @@ -26,9 +25,9 @@ class CancelWorkItemService extends BaseWorkItemService { } async cancel() { + this.workItem = await WorkItemModel.findOneByUpsInstanceUID(this.upsInstanceUID); - this.workItem = await this.findOneWorkItem(this.upsInstanceUID); - let procedureStepState = this.workItem.getString(dictionary.keyword.ProcedureStepState); + let procedureStepState = (await this.workItem.toDicomJson()).getString(dictionary.keyword.ProcedureStepState); if (procedureStepState === "IN PROGRESS") { //Only send cancel info event now, IMO @@ -58,34 +57,12 @@ class CancelWorkItemService extends BaseWorkItemService { } - /** - * - * @param {string} upsInstanceUID - * @returns - */ - async findOneWorkItem(upsInstanceUID) { - - let workItem = await workItemModel.findOne({ - upsInstanceUID: upsInstanceUID - }); - - if (!workItem) { - throw new DicomWebServiceError( - DicomWebStatusCodes.UPSDoesNotExist, - "The UPS instance not exist", - 404 - ); - } - - return new DicomJsonModel(workItem); - - } - async addCancelEvent() { let hitSubscriptions = await this.getHitSubscriptions(this.workItem); - let aeTitles = hitSubscriptions.map(v => v.aeTitle); - - this.addUpsEvent(UPS_EVENT_TYPE.CancelRequested, this.upsInstanceUID, this.cancelRequestBy(), aeTitles); + if (hitSubscriptions.length > 0 ) { + let aeTitles = hitSubscriptions.map(v => v.aeTitle); + this.addUpsEvent(UPS_EVENT_TYPE.CancelRequested, this.upsInstanceUID, this.cancelRequestBy(), aeTitles); + } } cancelRequestBy() { diff --git a/api/dicom-web/controller/UPS-RS/service/change-workItem-state.service.js b/api/dicom-web/controller/UPS-RS/service/change-workItem-state.service.js index 268c76bc..3e065759 100644 --- a/api/dicom-web/controller/UPS-RS/service/change-workItem-state.service.js +++ b/api/dicom-web/controller/UPS-RS/service/change-workItem-state.service.js @@ -1,14 +1,14 @@ const _ = require("lodash"); const moment = require("moment"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); const { DicomCode } = require("@models/DICOM/code"); -const workItemModel = require("@models/mongodb/models/workItems"); +const { WorkItemModel } = require("@dbModels/workitems.model"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); -const { BaseWorkItemService } = require("./base-workItem.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); const { UPS_EVENT_TYPE } = require("./workItem-event"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); class ChangeWorkItemStateService extends BaseWorkItemService { /** @@ -19,8 +19,8 @@ class ChangeWorkItemStateService extends BaseWorkItemService { constructor(req, res) { super(req, res); this.requestState = /** @type {Object[]} */(this.request.body).pop(); - /** @type {DicomJsonModel} */ - this.requestState = new DicomJsonModel(this.requestState); + /** @type {BaseDicomJson} */ + this.requestState = new BaseDicomJson(this.requestState); this.transactionUID = this.requestState.getString("00081195"); if (!this.transactionUID) { this.response.set("Warning", "299 Raccoon: The Transaction UID is missing."); @@ -36,11 +36,12 @@ class ChangeWorkItemStateService extends BaseWorkItemService { this.workItemTransactionUID = ""; } - async changeWorkItemState() { - await this.findOneWorkItem(); - - this.workItemState = this.workItem.getString("00741000"); - this.workItemTransactionUID = this.workItem.getString("00081195"); + async changeWorkItemState() { + this.workItem = await WorkItemModel.findOneByUpsInstanceUID(this.request.params.workItem); + /** @type { BaseDicomJson } */ + this.workItemDicomJson = await this.workItem.toDicomJson(); + this.workItemState = this.workItemDicomJson.getString("00741000"); + this.workItemTransactionUID = this.workItemDicomJson.getString("00081195"); let requestState = this.requestState.getString("00741000"); if (requestState === "IN PROGRESS") { @@ -51,44 +52,23 @@ class ChangeWorkItemStateService extends BaseWorkItemService { this.completeChange(); } - let updatedWorkItem = await workItemModel.findOneAndUpdate({ - upsInstanceUID: this.request.params.workItem - }, { + let updatedWorkItem = await WorkItemModel.updateOneByUpsInstanceUID(this.request.params.workItem, { + ...this.workItemDicomJson.dicomJson, ...this.requestState.dicomJson - }, { - new: true }); - let updatedWorkItemDicomJson = new DicomJsonModel(updatedWorkItem.toObject()); + let updatedWorkItemDicomJson = await updatedWorkItem.toDicomJson(); - let hitSubscriptions = await this.getHitSubscriptions(updatedWorkItemDicomJson); + let hitSubscriptions = await this.getHitSubscriptions(updatedWorkItem); if (hitSubscriptions.length === 0) return; let hitSubscriptionAeTitleArray = hitSubscriptions.map(sub => sub.aeTitle); - this.addUpsEvent(UPS_EVENT_TYPE.StateReport, updatedWorkItemDicomJson.dicomJson.upsInstanceUID, this.stateReportOf(updatedWorkItemDicomJson), hitSubscriptionAeTitleArray); + this.addUpsEvent(UPS_EVENT_TYPE.StateReport, updatedWorkItem.upsInstanceUID, this.stateReportOf(updatedWorkItemDicomJson), hitSubscriptionAeTitleArray); this.triggerUpsEvents(); } - async findOneWorkItem() { - - let workItem = await workItemModel.findOne({ - upsInstanceUID: this.request.params.workItem - }); - - if (!workItem) { - throw new DicomWebServiceError( - DicomWebStatusCodes.UPSDoesNotExist, - "The UPS instance not exist", - 404 - ); - } - - this.workItem = new DicomJsonModel(workItem); - - } - inProgressChange() { if (this.workItemState === "IN PROGRESS") { throw new DicomWebServiceError( @@ -166,9 +146,9 @@ class ChangeWorkItemStateService extends BaseWorkItemService { } ensureProgressInformationSequence() { - let progressInformation = _.get(this.workItem.dicomJson, "00741002.Value"); + let progressInformation = this.workItemDicomJson.getValues("00741002"); if (!progressInformation) { - _.set(this.workItem.dicomJson, "00741002", { + _.set(this.workItemDicomJson.dicomJson, "00741002", { vr: "SQ", Value: [] }); @@ -177,9 +157,9 @@ class ChangeWorkItemStateService extends BaseWorkItemService { supplementDiscontinuationReasonCode() { this.ensureProgressInformationSequence(); - let procedureStepCancellationDateTime = _.get(this.workItem.dicomJson, "00741002.Value.0.00404052"); + let procedureStepCancellationDateTime = _.get(this.workItemDicomJson.dicomJson, "00741002.Value.0.00404052"); if (!procedureStepCancellationDateTime) { - _.set(this.workItem.dicomJson, "00741002.Value.0.00404052", { + _.set(this.workItemDicomJson.dicomJson, "00741002.Value.0.00404052", { vr: "DT", Value: [ moment().format("YYYYMMDDhhmmss.SSSSSSZZ") @@ -187,9 +167,9 @@ class ChangeWorkItemStateService extends BaseWorkItemService { }); } - let reasonCodeMeaning = _.get(this.workItem.dicomJson, "00741002.Value.0.0074100E.Value.0.00080104"); + let reasonCodeMeaning = _.get(this.workItemDicomJson.dicomJson, "00741002.Value.0.0074100E.Value.0.00080104"); if (!reasonCodeMeaning) { - _.set(this.workItem.dicomJson, "00741002.Value.0.0074100E.Value.0", { + _.set(this.workItemDicomJson.dicomJson, "00741002.Value.0.0074100E.Value.0", { vr: "SQ", Value: [ { @@ -212,7 +192,7 @@ class ChangeWorkItemStateService extends BaseWorkItemService { } meetFinalStateRequirementsOfCompleted() { - let performedProcedure = _.get(this.workItem.dicomJson, "00741216"); + let performedProcedure = _.get(this.workItemDicomJson.dicomJson, "00741216"); if (performedProcedure && _.get(performedProcedure, "Value.0.00404050") && _.get(performedProcedure, "Value.0.00404051")) { diff --git a/api/dicom-web/controller/UPS-RS/service/create-workItem.service.js b/api/dicom-web/controller/UPS-RS/service/create-workItem.service.js index 90e1efea..d3355c9d 100644 --- a/api/dicom-web/controller/UPS-RS/service/create-workItem.service.js +++ b/api/dicom-web/controller/UPS-RS/service/create-workItem.service.js @@ -1,29 +1,39 @@ const _ = require("lodash"); -const workItemModel = require("@models/mongodb/models/workItems"); -const patientModel = require("@models/mongodb/models/patient"); +const { WorkItemModel } = require("@dbModels/workitems.model"); const { UIDUtils } = require("@dcm4che/util/UIDUtils"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); -const { BaseWorkItemService } = require("./base-workItem.service"); -const { SubscribeService } = require("./subscribe.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); +const { SubscribeService } = require("@api/dicom-web/controller/UPS-RS/service/subscribe.service"); const { UPS_EVENT_TYPE } = require("./workItem-event"); const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); class CreateWorkItemService extends BaseWorkItemService { constructor(req, res) { super(req, res); this.requestWorkItem = /** @type {Object[]} */(this.request.body).pop(); - /** @type {DicomJsonModel} */ - this.requestWorkItem = new DicomJsonModel(this.requestWorkItem); + /** @type {BaseDicomJson} */ + this.requestWorkItem = new BaseDicomJson(this.requestWorkItem); } async createUps() { let uid = _.get(this.request, "query.workitem", await UIDUtils.createUID() ); + await this.dataAdjustBeforeCreatingUps(uid); + await this.validateWorkItem(uid); + + let savedWorkItem = await WorkItemModel.createWorkItemAndPatient(this.requestWorkItem.dicomJson); + + this.triggerCreateEvent(savedWorkItem); + + return savedWorkItem; + } + + async dataAdjustBeforeCreatingUps(uid) { _.set(this.requestWorkItem.dicomJson, "upsInstanceUID", uid); _.set(this.requestWorkItem.dicomJson, "00080018", { vr: "UI", @@ -41,6 +51,11 @@ class CreateWorkItemService extends BaseWorkItemService { }); } + let patientId = this.requestWorkItem.getString("00100020"); + _.set(this.requestWorkItem.dicomJson, "patientID", patientId); + } + + async validateWorkItem(uid) { if (this.requestWorkItem.getString("00741000") !== "SCHEDULED") { throw new DicomWebServiceError( DicomWebStatusCodes.UPSNotScheduled, @@ -49,10 +64,6 @@ class CreateWorkItemService extends BaseWorkItemService { ); } - let patient = await this.findOneOrCreatePatient(); - - let workItem = new workItemModel(this.requestWorkItem.dicomJson); - if (await this.isUpsExist(uid)) { throw new DicomWebServiceError( DicomWebStatusCodes.DuplicateSOPinstance, @@ -60,62 +71,40 @@ class CreateWorkItemService extends BaseWorkItemService { 400 ); } - let savedWorkItem = await workItem.save(); - + } - let workItemDicomJson = new DicomJsonModel(savedWorkItem); - let hitGlobalSubscriptions = await this.getHitGlobalSubscriptions(workItemDicomJson); - for(let hitGlobalSubscription of hitGlobalSubscriptions) { + async triggerCreateEvent(workItem) { + let workItemDicomJson = await workItem.toDicomJson(); + let hitGlobalSubscriptions = await this.getHitGlobalSubscriptions(workItem); + for (let hitGlobalSubscription of hitGlobalSubscriptions) { let subscribeService = new SubscribeService(this.request, this.response); - subscribeService.upsInstanceUID = workItemDicomJson.dicomJson.upsInstanceUID; + subscribeService.upsInstanceUID = workItem.upsInstanceUID; subscribeService.deletionLock = hitGlobalSubscription.isDeletionLock; subscribeService.subscriberAeTitle = hitGlobalSubscription.aeTitle; await subscribeService.create(); } - let hitSubscriptions = await this.getHitSubscriptions(workItemDicomJson); - - if (hitSubscriptions) { + let hitSubscriptions = await this.getHitSubscriptions(workItem); + + if (hitSubscriptions?.length > 0 ) { let hitSubscriptionAeTitleArray = hitSubscriptions.map(sub => sub.aeTitle); - this.addUpsEvent(UPS_EVENT_TYPE.StateReport, workItemDicomJson.dicomJson.upsInstanceUID, this.stateReportOf(workItemDicomJson), hitSubscriptionAeTitleArray); + this.addUpsEvent(UPS_EVENT_TYPE.StateReport, workItem.upsInstanceUID, this.stateReportOf(await workItem.toDicomJson()), hitSubscriptionAeTitleArray); let assignedEventInformationArray = await this.getAssignedEventInformationArray( workItemDicomJson, _.get(workItemDicomJson.dicomJson, `${dictionary.keyword.ScheduledStationNameCodeSequence}`, false), _.get(workItemDicomJson.dicomJson, `${dictionary.keyword.ScheduledHumanPerformersSequence}`, false) ); - - for(let assignedEventInfo of assignedEventInformationArray) { - this.addUpsEvent(UPS_EVENT_TYPE.Assigned, workItemDicomJson.dicomJson.upsInstanceUID, assignedEventInfo, hitSubscriptionAeTitleArray); - } - } - - this.triggerUpsEvents(); - return workItem; - } - - async findOneOrCreatePatient() { - let patientId = this.requestWorkItem.getString("00100020"); - _.set(this.requestWorkItem.dicomJson, "patientID", patientId); - - /** @type {patientModel | null} */ - let patient = await patientModel.findOne({ - "00100020.Value": patientId - }); - - if (!patient) { - /** @type {patientModel} */ - let patientObj = new patientModel(this.requestWorkItem.dicomJson); - patient = await patientObj.save(); + for (let assignedEventInfo of assignedEventInformationArray) { + this.addUpsEvent(UPS_EVENT_TYPE.Assigned, workItem.upsInstanceUID, assignedEventInfo, hitSubscriptionAeTitleArray); + } } - return patient; + this.triggerUpsEvents(); } async isUpsExist(uid) { - return await workItemModel.findOne({ - upsInstanceUID: uid - }); + return await WorkItemModel.findOneByUpsInstanceUID(uid); } } diff --git a/api/dicom-web/controller/UPS-RS/service/get-workItem.service.js b/api/dicom-web/controller/UPS-RS/service/get-workItem.service.js index 97147800..17f9c561 100644 --- a/api/dicom-web/controller/UPS-RS/service/get-workItem.service.js +++ b/api/dicom-web/controller/UPS-RS/service/get-workItem.service.js @@ -1,9 +1,8 @@ const _ = require("lodash"); -const workItemsModel = require("@models/mongodb/models/workItems"); const { - convertAllQueryToDICOMTag, - convertRequestQueryToMongoQuery -} = require("../../QIDO-RS/service/QIDO-RS.service"); + QueryUpsDicomJsonFactory +} = require("../../QIDO-RS/service/query-dicom-json-factory"); +const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); class GetWorkItemService { constructor(req, res) { @@ -28,16 +27,15 @@ class GetWorkItemService { } async getUps() { - let mongoQuery = (await convertRequestQueryToMongoQuery(this.query)).$match; - let queryOptions = { - query: mongoQuery, + query: this.query, skip: this.skip_, limit: this.limit_, requestParams: this.request.params }; + let queryFactory = new QueryUpsDicomJsonFactory(queryOptions); - let docs = await workItemsModel.getDicomJson(queryOptions); + let docs = await queryFactory.getDicomJson(); return this.adjustDocs(docs); } @@ -60,7 +58,7 @@ class GetWorkItemService { if (!query[queryKey]) delete query[queryKey]; } - this.query = convertAllQueryToDICOMTag(query); + this.query = convertAllQueryToDicomTag(query); } } diff --git a/api/dicom-web/controller/UPS-RS/service/subscribe.service.js b/api/dicom-web/controller/UPS-RS/service/subscribe.service.js index e0abe7a0..4eb16b1c 100644 --- a/api/dicom-web/controller/UPS-RS/service/subscribe.service.js +++ b/api/dicom-web/controller/UPS-RS/service/subscribe.service.js @@ -1,17 +1,15 @@ const _ = require("lodash"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); -const { DicomCode } = require("@models/DICOM/code"); -const workItemModel = require("@models/mongodb/models/workItems"); -const subscriptionModel = require("@models/mongodb/models/upsSubscription"); -const globalSubscriptionModel = require("@models/mongodb/models/upsGlobalSubscription"); +const { WorkItemModel } = require("@dbModels/workitems.model"); +const { UpsSubscriptionModel } = require("@dbModels/upsSubscription"); +const { UpsGlobalSubscriptionModel } = require("@dbModels/upsGlobalSubscription"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); const { SUBSCRIPTION_STATE, SUBSCRIPTION_FIXED_UIDS } = require("@models/DICOM/ups"); -const { BaseWorkItemService } = require("./base-workItem.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); const { UPS_EVENT_TYPE } = require("./workItem-event"); -const { convertAllQueryToDICOMTag } = require("../../QIDO-RS/service/QIDO-RS.service"); +const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); class SubscribeService extends BaseWorkItemService { @@ -30,51 +28,25 @@ class SubscribeService extends BaseWorkItemService { } async create() { - - if (this.upsInstanceUID === SUBSCRIPTION_FIXED_UIDS.GlobalUID || + + if (this.upsInstanceUID === SUBSCRIPTION_FIXED_UIDS.GlobalUID || this.upsInstanceUID === SUBSCRIPTION_FIXED_UIDS.FilteredGlobalUID) { - this.query = convertAllQueryToDICOMTag(this.request.query); + this.query = convertAllQueryToDicomTag(this.request.query); await this.createOrUpdateGlobalSubscription(); } else { - let workItem = await this.findOneWorkItem(this.upsInstanceUID); + let workItem = await WorkItemModel.findOneByUpsInstanceUID(this.upsInstanceUID); await this.createOrUpdateSubscription(workItem); - this.addUpsEvent(UPS_EVENT_TYPE.StateReport, this.upsInstanceUID, this.stateReportOf(workItem), [this.subscriberAeTitle]); + this.addUpsEvent(UPS_EVENT_TYPE.StateReport, this.upsInstanceUID, this.stateReportOf(await workItem.toDicomJson()), [this.subscriberAeTitle]); } - - await this.triggerUpsEvents(); - } - - /** - * - * @param {string} upsInstanceUID - * @returns - */ - async findOneWorkItem(upsInstanceUID) { - - let workItem = await workItemModel.findOne({ - upsInstanceUID: upsInstanceUID - }); - - if (!workItem) { - throw new DicomWebServiceError( - DicomWebStatusCodes.UPSDoesNotExist, - "The UPS instance not exist", - 404 - ); - } - - return new DicomJsonModel(workItem); - + await this.triggerUpsEvents(); } //#region Subscription async findOneSubscription() { - - let subscription = await subscriptionModel.findOne({ - aeTitle: this.subscriberAeTitle - }); + + let subscription = await UpsSubscriptionModel.findOneByAeTitle(this.subscriberAeTitle); return subscription; @@ -82,49 +54,23 @@ class SubscribeService extends BaseWorkItemService { /** * - * @param {DicomJsonModel} workItem + * @param {any} workItem repository workItem * @returns */ async createOrUpdateSubscription(workItem) { - let subscription = await this.findOneSubscription(workItem); + let subscription = await this.findOneSubscription(); let subscribed = this.deletionLock ? SUBSCRIPTION_STATE.SUBSCRIBED_NO_LOCK : SUBSCRIPTION_STATE.SUBSCRIBED_LOCK; - await this.updateWorkItemSubscription(workItem, subscribed); + await workItem.subscribe(subscribed); + if (!subscription) { // Create - let subscriptionObj = new subscriptionModel({ - aeTitle: this.subscriberAeTitle, - workItems: [ - workItem.dicomJson._id - ], - isDeletionLock: this.deletionLock, - subscribed: subscribed - }); - - let createdSubscription = await subscriptionObj.save(); - return createdSubscription; + return await UpsSubscriptionModel.createSubscriptionForWorkItem(workItem, this.subscriberAeTitle, this.deletionLock, subscribed); } else { // Update - let updatedSubscription =await subscriptionModel.findOneAndUpdate({ - _id: subscription._id - }, { - $set: { - isDeletionLock: this.deletionLock, - subscribed: subscribed - }, - $addToSet: { - workItems: workItem.dicomJson._id - } - }); - subscription.isDeletionLock = this.deletionLock; - subscription.subscribed = subscribed; - return updatedSubscription; + return await UpsSubscriptionModel.updateSubscription(subscription, workItem, this.deletionLock, subscribed); } } - async updateWorkItemSubscription(workItem, subscription) { - workItem.dicomJson.subscribed = subscription; - await workItem.dicomJson.save(); - } //#endregion //#region Global Subscriptions @@ -141,36 +87,29 @@ class SubscribeService extends BaseWorkItemService { } if (!subscription) { //Create - let subscriptionObj = new globalSubscriptionModel({ + await UpsGlobalSubscriptionModel.createGlobalSubscription({ aeTitle: this.subscriberAeTitle, isDeletionLock: this.deletionLock, subscribed: subscribed, queryKeys: this.query }); - - let createdSubscription = await subscriptionObj.save(); } else { //Update - subscription.isDeletionLock = this.deletionLock; - subscription.subscribed = subscribed; - subscription.queryKeys = this.query; - await subscription.save(); + await UpsGlobalSubscriptionModel.updateRepositoryInstance(subscription, this.query, this.deletionLock, subscribed); } let notSubscribedWorkItems = await this.findNotSubscribedWorkItems(); - for(let notSubscribedWorkItem of notSubscribedWorkItems) { - let workItemDicomJson = new DicomJsonModel(notSubscribedWorkItem); - await this.createOrUpdateSubscription(workItemDicomJson); - - this.addUpsEvent(UPS_EVENT_TYPE.StateReport, workItemDicomJson.dicomJson.upsInstanceUID, this.stateReportOf(workItemDicomJson), [this.subscriberAeTitle]); + for (let notSubscribedWorkItem of notSubscribedWorkItems) { + let workItemDicomJson = await notSubscribedWorkItem.toDicomJson(); + await this.createOrUpdateSubscription(notSubscribedWorkItem); + + this.addUpsEvent(UPS_EVENT_TYPE.StateReport, notSubscribedWorkItem.upsInstanceUID, this.stateReportOf(workItemDicomJson), [this.subscriberAeTitle]); } } async findOneGlobalSubscription() { - - let globalSubscription = await globalSubscriptionModel.findOne({ - aeTitle: this.subscriberAeTitle - }); + + let globalSubscription = await UpsGlobalSubscriptionModel.findOneByAeTitle(this.subscriberAeTitle); return globalSubscription; @@ -178,19 +117,7 @@ class SubscribeService extends BaseWorkItemService { //#endregion async findNotSubscribedWorkItems() { - return await workItemModel.find({ - $or: [ - { - subscribed: SUBSCRIPTION_STATE.NOT_SUBSCRIBED - }, - { - subscribed: { - $exists: false - } - } - ] - - }) || []; + return await WorkItemModel.findNotSubscribedWorkItems(); } } diff --git a/api/dicom-web/controller/UPS-RS/service/suspend-subscription.service.js b/api/dicom-web/controller/UPS-RS/service/suspend-subscription.service.js index 3a3c47bc..038e04f6 100644 --- a/api/dicom-web/controller/UPS-RS/service/suspend-subscription.service.js +++ b/api/dicom-web/controller/UPS-RS/service/suspend-subscription.service.js @@ -1,10 +1,10 @@ const _ = require("lodash"); -const globalSubscriptionModel = require("@models/mongodb/models/upsGlobalSubscription"); +const { UpsGlobalSubscriptionModel } = require("@dbModels/upsGlobalSubscription"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); -const { BaseWorkItemService } = require("./base-workItem.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); class SuspendSubscribeService extends BaseWorkItemService { @@ -35,17 +35,12 @@ class SuspendSubscribeService extends BaseWorkItemService { } async deleteGlobalSubscription() { - - await globalSubscriptionModel.findOneAndDelete({ - aeTitle: this.subscriberAeTitle - }); + await UpsGlobalSubscriptionModel.deleteOneByAeTitle(this.subscriberAeTitle); } async isGlobalSubscriptionExist() { - return await globalSubscriptionModel.countDocuments({ - aeTitle: this.subscriberAeTitle - }) > 0; + return await UpsGlobalSubscriptionModel.getCountByAeTitle(this.subscriberAeTitle) > 0; } } diff --git a/api/dicom-web/controller/UPS-RS/service/unsubscribe.service.js b/api/dicom-web/controller/UPS-RS/service/unsubscribe.service.js index cfe13a3f..e0d8b816 100644 --- a/api/dicom-web/controller/UPS-RS/service/unsubscribe.service.js +++ b/api/dicom-web/controller/UPS-RS/service/unsubscribe.service.js @@ -1,16 +1,14 @@ const _ = require("lodash"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); const { DicomCode } = require("@models/DICOM/code"); -const workItemModel = require("@models/mongodb/models/workItems"); -const subscriptionModel = require("@models/mongodb/models/upsSubscription"); -const globalSubscriptionModel = require("@models/mongodb/models/upsGlobalSubscription"); +const { WorkItemModel } = require("@dbModels/workitems.model"); +const { UpsSubscriptionModel } = require("@dbModels/upsSubscription"); +const { UpsGlobalSubscriptionModel } = require("@dbModels/upsGlobalSubscription"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); const { SUBSCRIPTION_STATE, SUBSCRIPTION_FIXED_UIDS } = require("@models/DICOM/ups"); -const { BaseWorkItemService } = require("./base-workItem.service"); -const { convertAllQueryToDICOMTag } = require("../../QIDO-RS/service/QIDO-RS.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); class UnSubscribeService extends BaseWorkItemService { @@ -42,7 +40,7 @@ class UnSubscribeService extends BaseWorkItemService { await this.deleteGlobalSubscription(); } else { - let workItem = await this.findOneWorkItem(this.upsInstanceUID); + let workItem = await WorkItemModel.findOneByUpsInstanceUID(this.upsInstanceUID); if (!(await this.isSubscriptionExist())) { throw new DicomWebServiceError( @@ -59,67 +57,26 @@ class UnSubscribeService extends BaseWorkItemService { /** * - * @param {string} upsInstanceUID - * @returns {Promise} - */ - async findOneWorkItem(upsInstanceUID) { - - let workItem = await workItemModel.findOne({ - upsInstanceUID: upsInstanceUID - }); - - if (!workItem) { - throw new DicomWebServiceError( - DicomWebStatusCodes.UPSDoesNotExist, - "The UPS instance not exist", - 404 - ); - } - - return new DicomJsonModel(workItem); - - } - - /** - * - * @param {DicomJsonModel} workItem + * @param {any} workItem repository workItem */ async deleteSubscription(workItem) { - - await subscriptionModel.findOneAndUpdate({ - aeTitle: this.subscriberAeTitle, - workItems: workItem.dicomJson._id - }, { - $pull: { - workItems: workItem.dicomJson._id - } - }); - + await UpsSubscriptionModel.unsubscribe(this.subscriberAeTitle, workItem); } async deleteGlobalSubscription() { - await Promise.all([ - subscriptionModel.findOneAndDelete({ - aeTitle: this.subscriberAeTitle - }), - globalSubscriptionModel.findOneAndDelete({ - aeTitle: this.subscriberAeTitle - }) + UpsSubscriptionModel.deleteOneByAeTitle(this.subscriberAeTitle), + UpsGlobalSubscriptionModel.deleteOneByAeTitle(this.subscriberAeTitle) ]); } async isSubscriptionExist() { - return await subscriptionModel.countDocuments({ - aeTitle: this.subscriberAeTitle - }) > 0; + return await UpsSubscriptionModel.getCountByAeTitle(this.subscriberAeTitle) > 0; } async isGlobalSubscriptionExist() { - return await globalSubscriptionModel.countDocuments({ - aeTitle: this.subscriberAeTitle - }) > 0; + return await UpsGlobalSubscriptionModel.getCountByAeTitle(this.subscriberAeTitle) > 0; } } diff --git a/api/dicom-web/controller/UPS-RS/service/update-workItem.service.js b/api/dicom-web/controller/UPS-RS/service/update-workItem.service.js index 1ced5acc..16929f87 100644 --- a/api/dicom-web/controller/UPS-RS/service/update-workItem.service.js +++ b/api/dicom-web/controller/UPS-RS/service/update-workItem.service.js @@ -1,34 +1,32 @@ const _ = require("lodash"); -const workItemModel = require("@models/mongodb/models/workItems"); -const patientModel = require("@models/mongodb/models/patient"); +const { WorkItemModel } = require("@dbModels/workitems.model"); +const { PatientModel } = require("@dbModels/patient.model"); const { UIDUtils } = require("@dcm4che/util/UIDUtils"); const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); -const { BaseWorkItemService } = require("./base-workItem.service"); +const { BaseWorkItemService } = require("@api/dicom-web/controller/UPS-RS/service/base-workItem.service"); const { dictionary } = require("@models/DICOM/dicom-tags-dic"); const { UPS_EVENT_TYPE } = require("./workItem-event"); - - -const notAllowedAttributes = [ - "00080016", - "00080018", - "00100010", - "00100020", - "00100030", - "00100040", - "00380010", - "00380014", - "00081080", - "00081084", - "0040A370", - "00741224", - "00741000" -]; +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); class UpdateWorkItemService extends BaseWorkItemService { + static notAllowedAttributes = Object.freeze([ + "00080016", + "00080018", + "00100010", + "00100020", + "00100030", + "00100040", + "00380010", + "00380014", + "00081080", + "00081084", + "0040A370", + "00741224", + "00741000" + ]); /** * * @param {import('express').Request} req @@ -37,41 +35,42 @@ class UpdateWorkItemService extends BaseWorkItemService { constructor(req, res) { super(req, res); this.requestWorkItem = /** @type {Object[]} */(this.request.body).pop(); - /** @type {DicomJsonModel} */ - this.requestWorkItem = new DicomJsonModel(this.requestWorkItem); - this.workItem = null; + /** @type { BaseDicomJson } */ + this.requestWorkItem = new BaseDicomJson(this.requestWorkItem); this.transactionUID = null; } async updateUps() { this.transactionUID = this.requestWorkItem.getString("00081195"); - await this.findOneWorkItem(); + let workItem = await WorkItemModel.findOneByUpsInstanceUID(this.request.params.workItem); + /** @type { BaseDicomJson } */ + this.workItemDicomJson = await workItem.toDicomJson(); await this.checkRequestUpsIsValid(); this.adjustRequestWorkItem(); - let updatedWorkItem = await workItemModel.findOneAndUpdate({ - upsInstanceUID: this.workItem.dicomJson.upsInstanceUID - }, { + let updatedWorkItem = await WorkItemModel.updateOneByUpsInstanceUID(this.request.params.workItem, { ...this.requestWorkItem.dicomJson - }, { - new: true }); - let updateWorkItemDicomJson = new DicomJsonModel(updatedWorkItem); - let hitSubscriptions = await this.getHitSubscriptions(updateWorkItemDicomJson); + this.triggerUpdateWorkItemEvent(updatedWorkItem); + } + + async triggerUpdateWorkItemEvent(workItem) { + let updateWorkItemDicomJson = await workItem.toDicomJson(); + let hitSubscriptions = await this.getHitSubscriptions(workItem); if (hitSubscriptions.length === 0) { - return updatedWorkItem; + return workItem; } let hitSubscriptionAeTitleArray = hitSubscriptions.map(sub => sub.aeTitle); //Each time the SCP changes the Input Readiness State (0040,4041) Attribute for a UPS instance, the SCP shall send a UPS State Report Event to subscribed SCUs. let modifiedInputReadLineState = this.requestWorkItem.getString(`${dictionary.keyword.InputReadinessState}`); - let originalInputReadLineState = this.workItem.getString(`${dictionary.keyword.InputReadinessState}`); + let originalInputReadLineState = this.workItemDicomJson.getString(`${dictionary.keyword.InputReadinessState}`); if (modifiedInputReadLineState && modifiedInputReadLineState !== originalInputReadLineState) { this.addUpsEvent( UPS_EVENT_TYPE.StateReport, - this.workItem.dicomJson.upsInstanceUID, + this.request.params.workItem, this.stateReportOf(updateWorkItemDicomJson), hitSubscriptionAeTitleArray ); @@ -82,14 +81,14 @@ class UpdateWorkItemService extends BaseWorkItemService { this.triggerUpsEvents(); } - + addProgressInfoUpdatedEvent(workItemDicomJson, aeTitles) { let modifiedProcedureStepProgressInfo = _.get(this.requestWorkItem.dicomJson, dictionary.keyword.ProcedureStepProgressInformationSequence); - let originalProcedureStepProgressInfo = _.get(this.workItem.dicomJson , dictionary.keyword.ProcedureStepProgressInformationSequence); + let originalProcedureStepProgressInfo = _.get(this.workItemDicomJson.dicomJson, dictionary.keyword.ProcedureStepProgressInformationSequence); if (modifiedProcedureStepProgressInfo && !_.isEqual(modifiedProcedureStepProgressInfo, originalProcedureStepProgressInfo)) { this.addUpsEvent( UPS_EVENT_TYPE.ProgressReport, - this.workItem.dicomJson.upsInstanceUID, + this.request.params.workItem, this.progressReportOf(workItemDicomJson), aeTitles ); @@ -98,39 +97,22 @@ class UpdateWorkItemService extends BaseWorkItemService { addAssignedEvents(workItemDicomJson, aeTitles) { let modifiedPerformer = _.get(this.requestWorkItem.dicomJson, dictionary.keyword.ScheduledHumanPerformersSequence); - let originalPerformer = _.get(this.workItem.dicomJson, dictionary.keyword.ScheduledHumanPerformersSequence); + let originalPerformer = _.get(this.workItemDicomJson.dicomJson, dictionary.keyword.ScheduledHumanPerformersSequence); let performerUpdated = modifiedPerformer && !_.isEqual(modifiedPerformer, originalPerformer); let modifiedStationName = _.get(this.requestWorkItem.dicomJson, dictionary.keyword.ScheduledStationNameCodeSequence); - let originalStationName = _.get(this.workItem.dicomJson, dictionary.keyword.ScheduledStationNameCodeSequence); + let originalStationName = _.get(this.workItemDicomJson.dicomJson, dictionary.keyword.ScheduledStationNameCodeSequence); let stationNameUpdate = modifiedStationName && !_.isEqual(modifiedStationName, originalStationName); let assignedEventInformationArray = this.getAssignedEventInformationArray(workItemDicomJson, performerUpdated, stationNameUpdate); - for(let assignedEventInfo of assignedEventInformationArray) { - this.addUpsEvent(UPS_EVENT_TYPE.Assigned, workItemDicomJson.dicomJson.upsInstanceUID, assignedEventInfo, aeTitles); + for (let assignedEventInfo of assignedEventInformationArray) { + this.addUpsEvent(UPS_EVENT_TYPE.Assigned, this.request.params.workItem, assignedEventInfo, aeTitles); } } - async findOneWorkItem() { - - let workItem = await workItemModel.findOne({ - upsInstanceUID: this.request.params.workItem - }); - - if (!workItem) { - throw new DicomWebServiceError( - DicomWebStatusCodes.UPSDoesNotExist, - "The UPS instance not exist", - 404 - ); - } - - this.workItem = new DicomJsonModel(workItem.toObject()); - - } checkRequestUpsIsValid() { - let procedureState = this.workItem.getString("00741000"); + let procedureState = this.workItemDicomJson.getString("00741000"); const mappingMethod = { "SCHEDULED": () => { @@ -143,7 +125,7 @@ class UpdateWorkItemService extends BaseWorkItemService { } }, "IN PROGRESS": () => { - let foundUpsTransactionUID = this.workItem.getString("00081195"); + let foundUpsTransactionUID = this.workItemDicomJson.getString("00081195"); if (!this.transactionUID) { throw new DicomWebServiceError( DicomWebStatusCodes.UPSTransactionUIDNotCorrect, @@ -179,8 +161,8 @@ class UpdateWorkItemService extends BaseWorkItemService { * remove not allowed updating attribute in request work item */ adjustRequestWorkItem() { - for (let i = 0; i < notAllowedAttributes.length; i++) { - let notAllowedAttr = notAllowedAttributes[i]; + for (let i = 0; i < UpdateWorkItemService.notAllowedAttributes.length; i++) { + let notAllowedAttr = UpdateWorkItemService.notAllowedAttributes[i]; _.unset(this.requestWorkItem.dicomJson, notAllowedAttr); } } diff --git a/api/dicom-web/controller/UPS-RS/service/workItem-event.js b/api/dicom-web/controller/UPS-RS/service/workItem-event.js index 37a36322..6914170d 100644 --- a/api/dicom-web/controller/UPS-RS/service/workItem-event.js +++ b/api/dicom-web/controller/UPS-RS/service/workItem-event.js @@ -1,5 +1,5 @@ const { default: UID } = require("@dcm4che/data/UID"); -const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); +const { DicomJsonModel } = require("@dicom-json-model"); class WorkItemEvent { diff --git a/api/dicom-web/controller/UPS-RS/subscribe.js b/api/dicom-web/controller/UPS-RS/subscribe.js index 33b20a46..f1a11e13 100644 --- a/api/dicom-web/controller/UPS-RS/subscribe.js +++ b/api/dicom-web/controller/UPS-RS/subscribe.js @@ -1,20 +1,21 @@ const { SubscribeService -} = require("./service/subscribe.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/subscribe.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class SubscribeWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`Create Subscription, params: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`Create Subscription, params: ${this.paramsToString()}`); try { let service = new SubscribeService(this.request, this.response); @@ -25,23 +26,8 @@ class SubscribeWorkItemController extends Controller { .status(201) .end(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).json({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/suspend-subscription.js b/api/dicom-web/controller/UPS-RS/suspend-subscription.js index 0115b810..4e0dc215 100644 --- a/api/dicom-web/controller/UPS-RS/suspend-subscription.js +++ b/api/dicom-web/controller/UPS-RS/suspend-subscription.js @@ -6,21 +6,22 @@ const { SuspendSubscribeService -} = require("./service/suspend-subscription.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/suspend-subscription.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class SuspendSubscribeWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`Suspend Subscription, params: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`Suspend Subscription, params: ${this.paramsToString()}`); try { let service = new SuspendSubscribeService(this.request, this.response); @@ -31,23 +32,8 @@ class SuspendSubscribeWorkItemController extends Controller { .status(200) .end(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).json({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/unsubscribe.js b/api/dicom-web/controller/UPS-RS/unsubscribe.js index 2fae701d..5d455821 100644 --- a/api/dicom-web/controller/UPS-RS/unsubscribe.js +++ b/api/dicom-web/controller/UPS-RS/unsubscribe.js @@ -1,20 +1,21 @@ const { UnSubscribeService -} = require("./service/unsubscribe.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/unsubscribe.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class UnSubscribeWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`UnSubscription, params: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`UnSubscription, params: ${this.paramsToString()}`); try { let service = new UnSubscribeService(this.request, this.response); @@ -25,23 +26,8 @@ class UnSubscribeWorkItemController extends Controller { .status(200) .end(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).json({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/UPS-RS/update-workItem.js b/api/dicom-web/controller/UPS-RS/update-workItem.js index 466636d6..ac5d955a 100644 --- a/api/dicom-web/controller/UPS-RS/update-workItem.js +++ b/api/dicom-web/controller/UPS-RS/update-workItem.js @@ -1,20 +1,21 @@ const { UpdateWorkItemService -} = require("./service/update-workItem.service"); +} = require("@api/dicom-web/controller/UPS-RS/service/update-workItem.service"); const { ApiLogger } = require("../../../../utils/logs/api-logger"); const { Controller } = require("../../../controller.class"); const { DicomWebServiceError } = require("@error/dicom-web-service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); class UpdateWorkItemController extends Controller { constructor(req, res) { super(req, res); + this.apiLogger = new ApiLogger(this.request, "UPS-RS"); } async mainProcess() { - let apiLogger = new ApiLogger(this.request, "UPS-RS"); - apiLogger.addTokenValue(); - apiLogger.logger.info(`Update workItem, params: ${this.paramsToString()}`); + this.apiLogger.addTokenValue(); + this.apiLogger.logger.info(`Update workItem, params: ${this.paramsToString()}`); try { let service = new UpdateWorkItemService(this.request, this.response); @@ -25,23 +26,8 @@ class UpdateWorkItemController extends Controller { .status(200) .end(); } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - if (e instanceof DicomWebServiceError) { - return this.response.status(e.code).json({ - status: e.status, - message: e.message - }); - } - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: "An Server Exception Occurred" - })); + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); } } } diff --git a/api/dicom-web/controller/WADO-RS/bulkdata/bulkdata.js b/api/dicom-web/controller/WADO-RS/bulkdata/bulkdata.js deleted file mode 100644 index 2c2fa6bf..00000000 --- a/api/dicom-web/controller/WADO-RS/bulkdata/bulkdata.js +++ /dev/null @@ -1,49 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { BulkDataService } = require("./service/bulkdata"); -const { getInternalServerErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); - -class BulkDataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get bulk data ${this.request.params.binaryValuePath}\ -, from StudyInstanceUID: ${this.request.params.studyUID}\ -, SeriesInstanceUID: ${this.request.params.seriesUID}\ -, SOPInstanceUID: ${this.request.params.instanceUID}`); - - let bulkDataService = new BulkDataService(this.request, this.response); - - try { - let bulkData = await bulkDataService.getSpecificBulkData(); - await bulkDataService.writeBulkData(bulkData); - bulkDataService.multipartWriter.writeFinalBoundary(); - return this.response.end(); - } catch(e) { - apiLogger.logger.error(e); - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function(req, res) { - let bulkDataController = new BulkDataController(req, res); - - await bulkDataController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/bulkdata/instance.js b/api/dicom-web/controller/WADO-RS/bulkdata/instance.js deleted file mode 100644 index 67ba0077..00000000 --- a/api/dicom-web/controller/WADO-RS/bulkdata/instance.js +++ /dev/null @@ -1,60 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { BulkDataService } = require("./service/bulkdata"); -const { getInternalServerErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); -const dicomModel = require("../../../../../models/mongodb/models/dicom"); - -class InstanceBulkDataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get bulk data from StudyInstanceUID: ${this.request.params.studyUID}\ -, SeriesInstanceUID: ${this.request.params.seriesUID}\ -, SOPInstanceUID: ${this.request.params.instanceUID}`); - - let bulkDataService = new BulkDataService(this.request, this.response); - - try { - let bulkDataArray = await bulkDataService.getInstanceBulkData(); - for (let bulkData of bulkDataArray) { - await bulkDataService.writeBulkData(bulkData); - } - - let dicomInstancePathObj = await dicomModel.getPathOfInstance({ - studyUID: this.request.params.studyUID, - seriesUID: this.request.params.seriesUID, - instanceUID: this.request.params.instanceUID - }); - - await bulkDataService.writeBulkData(dicomInstancePathObj); - - bulkDataService.multipartWriter.writeFinalBoundary(); - return this.response.end(); - } catch(e) { - apiLogger.logger.error(e); - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function(req, res) { - let instanceBulkDataController = new InstanceBulkDataController(req, res); - - await instanceBulkDataController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/bulkdata/retrieveBulkData.controller.js b/api/dicom-web/controller/WADO-RS/bulkdata/retrieveBulkData.controller.js new file mode 100644 index 00000000..98fc88b8 --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/bulkdata/retrieveBulkData.controller.js @@ -0,0 +1,51 @@ +const { Controller } = require("@root/api/controller.class"); +const { StudyBulkDataFactory, BulkDataService } = require("@api/dicom-web/controller/WADO-RS/bulkdata/service/bulkdata"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StudyImagePathFactory } = require("@api/dicom-web/controller/WADO-RS/service/WADO-RS.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseBulkDataController extends Controller { + constructor(req, res) { + super(req, res); + this.bulkDataService = new BulkDataService(this.request, this.response, this.request.bulkDataFactoryType); + } + + async mainProcess() { + try { + + let bulkData = await this.bulkDataService.getBulkData(); + if (Array.isArray(bulkData)) { + await this.responseBulkDataArray(bulkData); + } else { + await this.responseBulkData(bulkData); + } + + this.bulkDataService.multipartWriter.writeFinalBoundary(); + return this.response.end(); + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } + + async responseBulkDataArray(bulkDataArray) { + for (let bulkData of bulkDataArray) { + await this.bulkDataService.writeBulkData(bulkData); + } + + let imagePathFactory = new this.request.imagePathFactoryType({ + ...this.request.params + }); + await imagePathFactory.getImagePaths(); + + for (let imagePathObj of imagePathFactory.imagePaths) { + await this.bulkDataService.writeBulkData(imagePathObj); + } + } + + async responseBulkData(bulkData) { + await this.bulkDataService.writeBulkData(bulkData); + } +} + +module.exports.BaseBulkDataController = BaseBulkDataController; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/bulkdata/series.js b/api/dicom-web/controller/WADO-RS/bulkdata/series.js deleted file mode 100644 index 299d6eef..00000000 --- a/api/dicom-web/controller/WADO-RS/bulkdata/series.js +++ /dev/null @@ -1,60 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { BulkDataService } = require("./service/bulkdata"); -const dicomSeriesModel = require("../../../../../models/mongodb/models/dicomSeries"); -const { getInternalServerErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); - -class SeriesBulkDataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get bulk data from StudyInstanceUID: ${this.request.params.studyUID}\ -, SeriesInstanceUID: ${this.request.params.seriesUID}`); - - let bulkDataService = new BulkDataService(this.request, this.response); - - try { - let bulkDataArray = await bulkDataService.getSeriesBulkData(); - for (let bulkData of bulkDataArray) { - await bulkDataService.writeBulkData(bulkData); - } - - let dicomInstancePathObjArray = await dicomSeriesModel.getPathGroupOfInstances({ - studyUID: this.request.params.studyUID, - seriesUID: this.request.params.seriesUID - }); - - for (let instancePathObj of dicomInstancePathObjArray) { - await bulkDataService.writeBulkData(instancePathObj); - } - - bulkDataService.multipartWriter.writeFinalBoundary(); - return this.response.end(); - } catch (e) { - apiLogger.logger.error(e); - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function (req, res) { - let seriesBulkDataController = new SeriesBulkDataController(req, res); - - await seriesBulkDataController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/bulkdata/service/bulkdata.js b/api/dicom-web/controller/WADO-RS/bulkdata/service/bulkdata.js index 71ac34d8..e297f5d4 100644 --- a/api/dicom-web/controller/WADO-RS/bulkdata/service/bulkdata.js +++ b/api/dicom-web/controller/WADO-RS/bulkdata/service/bulkdata.js @@ -1,7 +1,6 @@ const fs = require("fs"); const path = require("path"); -const dicomBulkDataModel = require("../../../../../../models/mongodb/models/dicomBulkData"); -const dicomModel = require("../../../../../../models/mongodb/models/dicom"); +const { DicomBulkDataModel } = require("@dbModels/dicomBulkData.model"); const { MultipartWriter } = require("../../../../../../utils/multipartWriter"); const { streamToBuffer } = require("@jorgeferrero/stream-to-buffer"); const { raccoonConfig } = require("../../../../../../config-class"); @@ -11,23 +10,25 @@ class BulkDataService { * * @param {import("express").Request} req * @param {import("express").Response} res + * @param {typeof StudyBulkDataFactory | typeof SeriesBulkDataFactory | typeof InstanceBulkDataFactory | typeof SpecificBulkDataFactory } bulkDataFactory */ - constructor(req, res) { + constructor(req, res, bulkDataFactory) { this.request = req; this.response = res; + this.bulkDataFactory = new bulkDataFactory({ ...this.request.params }); this.multipartWriter = new MultipartWriter([], req, res); this.multipartWriter.setHeaderMultipartRelatedContentType("application/octet-stream"); } /** * - * @param {import("../../../../../../utils/typeDef/bulkdata").BulkData | - * import("../../../../../../utils/typeDef/WADO-RS/WADO-RS.def").ImagePathObj } bulkData + * @param {import("@root/utils/typeDef/bulkdata").BulkData | + * import("@root/utils/typeDef/dicomImage").ImagePathObj } bulkData */ async writeBulkData(bulkData) { let absFilename; // is imagePathObj - if(bulkData.instancePath) { + if (bulkData.instancePath) { absFilename = bulkData.instancePath; } else { absFilename = path.join(raccoonConfig.dicomWebConfig.storeRootPath, bulkData.filename); @@ -38,7 +39,7 @@ class BulkDataService { this.multipartWriter.writeBoundary(); this.multipartWriter.writeContentType("application/octet-stream"); this.multipartWriter.writeContentLength(fileBuffer.length); - + let urlPath = `/dicom-web/studies/${bulkData.studyUID}/series/${bulkData.seriesUID}/instances/${bulkData.instanceUID}/bulkdata/${bulkData.binaryValuePath}`; if (bulkData.instancePath) { urlPath = `/dicom-web/studies/${bulkData.studyUID}/series/${bulkData.seriesUID}/instances/${bulkData.instanceUID}`; @@ -47,98 +48,107 @@ class BulkDataService { this.multipartWriter.writeBufferData(fileBuffer); } - async getSpecificBulkData() { + async getBulkData() { + return await this.bulkDataFactory.getBulkData(); + } - let { - studyUID, - seriesUID, - instanceUID, - binaryValuePath - } = this.request.params; - - let bulkData = await dicomBulkDataModel.findOne({ - $and: [ - { - studyUID - }, - { - seriesUID - }, - { - instanceUID - }, - { - binaryValuePath: { - $regex: `^${binaryValuePath}`, - $options: "gm" - } - } - ] - }).exec(); - - return bulkData; +} + +class BulkDataFactory { + /** + * + * @param {Pick} uids + */ + constructor(uids) { + /** @type {Pick} */ + this.uids = uids; + } + + getBulkData() { + throw new Error("Abstract method, not implement"); + } +} + +class StudyBulkDataFactory extends BulkDataFactory { + constructor(uids) { + super(uids); } - async getStudyBulkData() { + async getBulkData() { let { studyUID - } = this.request.params; + } = this.uids; - let studyBulkDataArray = await dicomBulkDataModel.find({ - $and: [ - { - studyUID - } - ] - }).exec(); + return await DicomBulkDataModel.findStudyBulkData({ studyUID }); + } +} - return studyBulkDataArray; +class SeriesBulkDataFactory extends BulkDataFactory { + constructor(uids) { + super(uids); } - async getSeriesBulkData() { + async getBulkData() { let { studyUID, seriesUID - } = this.request.params; - - let seriesBulkDataArray = await dicomBulkDataModel.find({ - $and: [ - { - studyUID - }, - { - seriesUID - } - ] - }).exec(); - - return seriesBulkDataArray; + } = this.uids; + + return await DicomBulkDataModel.findSeriesBulkData({ + studyUID, + seriesUID + }); + + } +} + + +class InstanceBulkDataFactory extends BulkDataFactory { + constructor(uids) { + super(uids); } - async getInstanceBulkData() { + async getBulkData() { let { studyUID, seriesUID, instanceUID - } = this.request.params; - - let instanceBulkDataArray = await dicomBulkDataModel.find({ - $and: [ - { - studyUID - }, - { - seriesUID - }, - { - instanceUID - } - ] - }).exec(); - - return instanceBulkDataArray; + } = this.uids; + + return await DicomBulkDataModel.findInstanceBulkData({ + studyUID, + seriesUID, + instanceUID + }); } } +class SpecificBulkDataFactory extends BulkDataFactory { + constructor(uids) { + super(uids); + } + + async getBulkData() { + + let { + studyUID, + seriesUID, + instanceUID, + binaryValuePath + } = this.uids; + + return await DicomBulkDataModel.findSpecificBulkData({ + studyUID, + seriesUID, + instanceUID, + binaryValuePath + }); + + } +} -module.exports.BulkDataService = BulkDataService; \ No newline at end of file +module.exports.BulkDataService = BulkDataService; +module.exports.StudyBulkDataFactory = StudyBulkDataFactory; +module.exports.SeriesBulkDataFactory = SeriesBulkDataFactory; +module.exports.InstanceBulkDataFactory = InstanceBulkDataFactory; +module.exports.SpecificBulkDataFactory = SpecificBulkDataFactory; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/bulkdata/study.js b/api/dicom-web/controller/WADO-RS/bulkdata/study.js deleted file mode 100644 index 0b6dc452..00000000 --- a/api/dicom-web/controller/WADO-RS/bulkdata/study.js +++ /dev/null @@ -1,58 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { BulkDataService } = require("./service/bulkdata"); -const dicomStudyModel = require("../../../../../models/mongodb/models/dicomStudy"); -const { getInternalServerErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); - -class StudyBulkDataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get bulk data from StudyInstanceUID: ${this.request.params.studyUID}`); - - let bulkDataService = new BulkDataService(this.request, this.response); - - try { - let bulkDataArray = await bulkDataService.getStudyBulkData(); - for (let bulkData of bulkDataArray) { - await bulkDataService.writeBulkData(bulkData); - } - - let dicomInstancePathObjArray = await dicomStudyModel.getPathGroupOfInstances({ - studyUID: this.request.params.studyUID - }); - - for(let instancePathObj of dicomInstancePathObjArray) { - await bulkDataService.writeBulkData(instancePathObj); - } - - bulkDataService.multipartWriter.writeFinalBoundary(); - return this.response.end(); - } catch(e) { - apiLogger.logger.error(e); - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function(req, res) { - let studyBulkDataController = new StudyBulkDataController(req, res); - - await studyBulkDataController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/deletion/delete.controller.js b/api/dicom-web/controller/WADO-RS/deletion/delete.controller.js new file mode 100644 index 00000000..76292723 --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/deletion/delete.controller.js @@ -0,0 +1,34 @@ +const { Controller } = require("@root/api/controller.class"); +const { DeleteService } = require("@api/dicom-web/controller/WADO-RS/deletion/service/delete"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseDeleteController extends Controller { + constructor(req, res) { + super(req, res); + } + + async mainProcess() { + let deleteService = new DeleteService(this.request, this.response, this.request.dicomLevel); + + try { + await deleteService.delete(); + + return this.response.status(200).json({ + Details: this.getDeleteSuccessfulMessage(), + HttpStatus: 200, + Message: "Delete Successful", + Method: "DELETE" + }); + } catch(e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } + + getDeleteSuccessfulMessage() { + return `Delete ${this.request.dicomLevel} permanently, ${JSON.stringify(this.request.params)}`; + } + +} + +module.exports.BaseDeleteController = BaseDeleteController; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/deletion/instance.js b/api/dicom-web/controller/WADO-RS/deletion/instance.js deleted file mode 100644 index 5ccd7c2e..00000000 --- a/api/dicom-web/controller/WADO-RS/deletion/instance.js +++ /dev/null @@ -1,55 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { DeleteService } = require("./service/delete"); -const { getInternalServerErrorMessage, getNotFoundErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { NotFoundInstanceError } = require("../../../../../error/dicom-instance"); - -class DeleteInstanceController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - let deleteService = new DeleteService(this.request, this.response, "instance"); - - try { - await deleteService.delete(); - - return this.response.status(200).json({ - Details: `Delete Study permanently, StudyInstanceUID: ${this.request.params.studyUID}, SeriesInstanceUID: ${this.request.params.seriesUID}, SOPInstanceUID: ${this.request.params.instanceUID}`, - HttpStatus: 200, - Message: "Delete Successful", - Method: "DELETE" - }); - } catch(e) { - - if (e instanceof NotFoundInstanceError) { - return this.response.status(404).json( - getNotFoundErrorMessage(e.message) - ); - } - - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function(req, res) { - let deleteStudyController = new DeleteInstanceController(req, res); - - await deleteStudyController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/deletion/series.js b/api/dicom-web/controller/WADO-RS/deletion/series.js deleted file mode 100644 index efa106bd..00000000 --- a/api/dicom-web/controller/WADO-RS/deletion/series.js +++ /dev/null @@ -1,55 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { DeleteService } = require("./service/delete"); -const { getInternalServerErrorMessage, getNotFoundErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { NotFoundInstanceError } = require("../../../../../error/dicom-instance"); - -class DeleteSeriesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - let deleteService = new DeleteService(this.request, this.response, "series"); - - try { - await deleteService.delete(); - - return this.response.status(200).json({ - Details: `Delete Series permanently, StudyInstanceUID: ${this.request.params.studyUID}, SeriesInstanceUID: ${this.request.params.seriesUID}`, - HttpStatus: 200, - Message: "Delete Successful", - Method: "DELETE" - }); - } catch(e) { - - if (e instanceof NotFoundInstanceError) { - return this.response.status(404).json( - getNotFoundErrorMessage(e.message) - ); - } - - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function(req, res) { - let deleteStudyController = new DeleteSeriesController(req, res); - - await deleteStudyController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/deletion/service/delete.js b/api/dicom-web/controller/WADO-RS/deletion/service/delete.js index d8129f68..977a9d2e 100644 --- a/api/dicom-web/controller/WADO-RS/deletion/service/delete.js +++ b/api/dicom-web/controller/WADO-RS/deletion/service/delete.js @@ -1,7 +1,7 @@ const _ = require("lodash"); -const dicomStudyModel = require("../../../../../../models/mongodb/models/dicomStudy"); -const dicomSeriesModel = require("../../../../../../models/mongodb/models/dicomSeries"); -const dicomModel = require("../../../../../../models/mongodb/models/dicom"); +const { StudyModel } = require("@dbModels/study.model"); +const { SeriesModel } = require("@dbModels/series.model"); +const { InstanceModel } = require("@dbModels/instance.model"); const fsP = require("fs/promises"); const { NotFoundInstanceError } = require("../../../../../../error/dicom-instance"); @@ -28,7 +28,7 @@ class DeleteService { } async deleteStudy() { - let study = await dicomStudyModel.findOne({ + let study = await StudyModel.findOneByDicomUID({ ...this.request.params }); @@ -46,7 +46,7 @@ class DeleteService { } async deleteSeries() { - let aSeries = await dicomSeriesModel.findOne({ + let aSeries = await SeriesModel.findOneByDicomUID({ ...this.request.params }); @@ -64,7 +64,7 @@ class DeleteService { async deleteInstance() { - let instance = await dicomModel.findOne({ + let instance = await InstanceModel.findOneByDicomUID({ ...this.request.params }); diff --git a/api/dicom-web/controller/WADO-RS/deletion/study.js b/api/dicom-web/controller/WADO-RS/deletion/study.js deleted file mode 100644 index fe3ca6b2..00000000 --- a/api/dicom-web/controller/WADO-RS/deletion/study.js +++ /dev/null @@ -1,55 +0,0 @@ -const mongoose = require("mongoose"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { DeleteService } = require("./service/delete"); -const { getInternalServerErrorMessage, getNotFoundErrorMessage } = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { NotFoundInstanceError } = require("../../../../../error/dicom-instance"); - -class DeleteStudyController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - let deleteService = new DeleteService(this.request, this.response, "study"); - - try { - await deleteService.delete(); - - return this.response.status(200).json({ - Details: `Delete Study permanently, StudyInstanceUID: ${this.request.params.studyUID}`, - HttpStatus: 200, - Message: "Delete Successful", - Method: "DELETE" - }); - } catch(e) { - - if (e instanceof NotFoundInstanceError) { - return this.response.status(404).json( - getNotFoundErrorMessage(e.message) - ); - } - - return this.response.status(500).json( - getInternalServerErrorMessage("An exception occur") - ); - } - - } -} - - -/** - * - * @param {import("express").Request} - * @param {import("express").Response} - * @returns - */ -module.exports = async function(req, res) { - let deleteStudyController = new DeleteStudyController(req, res); - - await deleteStudyController.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/metadata/retrieveInstanceMetadata.js b/api/dicom-web/controller/WADO-RS/metadata/retrieveInstanceMetadata.js deleted file mode 100644 index eab95795..00000000 --- a/api/dicom-web/controller/WADO-RS/metadata/retrieveInstanceMetadata.js +++ /dev/null @@ -1,66 +0,0 @@ -const mongoose = require("mongoose"); -const _ = require("lodash"); -const fs = require("fs"); -const path = require("path"); -const fileExist = require("../../../../../utils/file/fileExist"); -const wadoService = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { Controller } = require("../../../../controller.class"); -const dicomModel = require("../../../../../models/mongodb/models/dicom"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); - -class RetrieveInstanceMetadataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - - apiLogger.addTokenValue(); - apiLogger.logger.info(`[WADO-RS] [Get Study's Series' Instance Metadata] [instance UID: ${this.request.params.instanceUID}, series UID: ${this.request.params.seriesUID}, study UID: ${this.request.params.studyUID}]`); - try { - let responseMetadata = []; - - let imagePathObj = await dicomModel.getPathOfInstance(this.request.params); - if (imagePathObj) { - let instanceDir = path.dirname(imagePathObj.instancePath); - let metadataPath = path.join(instanceDir, `${imagePathObj.instanceUID}.metadata.json`); - if (await fileExist(metadataPath)) { - let metadataJsonStr = fs.readFileSync(metadataPath, { encoding: "utf-8" }); - let metadataJson = JSON.parse(metadataJsonStr); - wadoService.addHostnameOfBulkDataUrl(metadataJson, this.request); - responseMetadata.push(metadataJson); - } - this.response.writeHead(200, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(responseMetadata)); - } - - this.response.writeHead(204); - return this.response.end(JSON.stringify( - errorResponse.getNotFoundErrorMessage( - `Not found metadata of instance UID: ${this.request.params.instanceUID}, series UID: ${this.request.params.seriesUID}, study UID: ${this.request.params.studyUID}` - ) - )); - } catch(e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - console.error(errorStr); - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(); - } - } -} -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function(req, res) { - let controller = new RetrieveInstanceMetadataController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/metadata/retrieveMetadata.controller.js b/api/dicom-web/controller/WADO-RS/metadata/retrieveMetadata.controller.js new file mode 100644 index 00000000..e0de42f2 --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/metadata/retrieveMetadata.controller.js @@ -0,0 +1,34 @@ +const { Controller } = require("@root/api/controller.class"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StudyImagePathFactory } = require("@api/dicom-web/controller/WADO-RS/service/WADO-RS.service"); +const { MetadataService } = require("../service/metadata.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseRetrieveMetadataController extends Controller { + constructor(req, res) { + super(req, res); + } + + async mainProcess() { + let metadataService = new MetadataService(this.request, this.request.imagePathFactory); + + try { + let responseMetadata = await metadataService.getMetadata(this.request.params); + if (responseMetadata.length > 0) { + this.response.writeHead(200, { + "Content-Type": "application/dicom+json" + }); + return this.response.end(JSON.stringify(responseMetadata)); + } + + this.response.writeHead(204); + return this.response.end(); + + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +module.exports.BaseRetrieveMetadataController = BaseRetrieveMetadataController; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/metadata/retrieveSeriesMetadata.js b/api/dicom-web/controller/WADO-RS/metadata/retrieveSeriesMetadata.js deleted file mode 100644 index a340a3d3..00000000 --- a/api/dicom-web/controller/WADO-RS/metadata/retrieveSeriesMetadata.js +++ /dev/null @@ -1,68 +0,0 @@ -const mongoose = require("mongoose"); -const _ = require("lodash"); -const fs = require("fs"); -const path = require("path"); -const fileExist = require("../../../../../utils/file/fileExist"); -const wadoService = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); - -class RetrieveSeriesMetadataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - - apiLogger.logger.info(`[WADO-RS] [Get Study's Series' Instances Metadata] [series UID: ${this.request.params.seriesUID}, study UID: ${this.request.params.studyUID}]`); - try { - let responseMetadata = []; - - let imagesPathList = await mongoose.model("dicomSeries").getPathGroupOfInstances(this.request.params); - if (imagesPathList.length > 0) { - for (let imagePathObj of imagesPathList) { - let instanceDir = path.dirname(imagePathObj.instancePath); - let metadataPath = path.join(instanceDir, `${imagePathObj.instanceUID}.metadata.json`); - if (await fileExist(metadataPath)) { - let metadataJsonStr = fs.readFileSync(metadataPath, { encoding: "utf-8" }); - let metadataJson = JSON.parse(metadataJsonStr); - wadoService.addHostnameOfBulkDataUrl(metadataJson, this.request); - responseMetadata.push(metadataJson); - } - } - this.response.writeHead(200, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(responseMetadata)); - } - - this.response.writeHead(204); - return this.response.end(JSON.stringify( - errorResponse.getNotFoundErrorMessage( - `Not found metadata of series UID:${this.request.params.seriesUID} study UID: ${this.request.params.studyUID}` - ) - )); - } catch(e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - console.error(errorStr); - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(); - } - } -} -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function(req, res) { - let controller = new RetrieveSeriesMetadataController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/metadata/retrieveStudyMetadata.js b/api/dicom-web/controller/WADO-RS/metadata/retrieveStudyMetadata.js deleted file mode 100644 index 63e31620..00000000 --- a/api/dicom-web/controller/WADO-RS/metadata/retrieveStudyMetadata.js +++ /dev/null @@ -1,71 +0,0 @@ -const mongoose = require("mongoose"); -const _ = require("lodash"); -const fs = require("fs"); -const path = require("path"); -const fileExist = require("../../../../../utils/file/fileExist"); -const wadoService = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); - -class RetrieveStudyMetadataController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get Study's Instances Metadata [study UID: ${this.request.params.studyUID}]`); - - try { - let responseMetadata = []; - - let pathGroupOfInstancesInStudy = await mongoose.model("dicomStudy").getPathGroupOfInstances(this.request.params); - - if (pathGroupOfInstancesInStudy.length > 0) { - - for (let imagePathObj of pathGroupOfInstancesInStudy) { - let instanceDir = path.dirname(imagePathObj.instancePath); - let metadataPath = path.join(instanceDir, `${imagePathObj.instanceUID}.metadata.json`); - if (await fileExist(metadataPath)) { - let metadataJsonStr = fs.readFileSync(metadataPath, { encoding: "utf-8" }); - let metadataJson = JSON.parse(metadataJsonStr); - wadoService.addHostnameOfBulkDataUrl(metadataJson, this.request); - responseMetadata.push(metadataJson); - } - } - - this.response.writeHead(200, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(responseMetadata)); - } - - this.response.writeHead(204); - return this.response.end(); - - } catch(e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(); - } - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function(req, res) { - let controller = new RetrieveStudyMetadataController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/rendered/instanceFrames.js b/api/dicom-web/controller/WADO-RS/rendered/instanceFrames.js deleted file mode 100644 index 7df99692..00000000 --- a/api/dicom-web/controller/WADO-RS/rendered/instanceFrames.js +++ /dev/null @@ -1,71 +0,0 @@ -const _ = require("lodash"); -const renderedService = require("../service/rendered.service"); -const { InstanceImagePathFactory } = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { Controller } = require("../../../../controller.class"); - -class RetrieveRenderedInstanceFramesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - this.apiLogger = new ApiLogger(this.request, "WADO-RS"); - this.apiLogger.addTokenValue(); - - let { - studyUID, - seriesUID, - instanceUID, - frameNumber - } = this.request.params; - - this.apiLogger.logger.info(`Get study's series' rendered instances' frames, study UID: ${studyUID}, series UID: ${seriesUID}, instance UID: ${instanceUID}, frame: ${frameNumber}`); - - let headerAccept = _.get(this.request.headers, "accept", ""); - if (!headerAccept.includes("*/*") && !headerAccept.includes("image/jpeg")) { - let badRequestMessage = errorResponse.getBadRequestErrorMessage(`header accept only allow */* or image/jpeg , exception : ${headerAccept}`); - this.response.writeHead(badRequestMessage.HttpStatus, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(badRequestMessage)); - } - - try { - let renderedImageMultipartWriter = new renderedService.RenderedImageMultipartWriter( - this.request, - this.response, - InstanceImagePathFactory, - renderedService.InstanceFramesListWriter - ); - - let buffer = await renderedImageMultipartWriter.write(); - - this.apiLogger.logger.info(`Get instance's frame successfully, instance UID: ${instanceUID}, frame number: ${JSON.stringify(frameNumber)}`); - - if (buffer instanceof Buffer) { - return this.response.end(buffer, "binary"); - } - - return this.response.end(); - } catch(e) { - console.error(e); - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify(e, Object.getOwnPropertyNames(e), 4), "utf8"); - } - } -} -/** - * - * @param {import("http").incomingMessage} req - * @param {import("http").ServerResponse} res - * @returns - */ -module.exports = async function(req, res) { - let controller = new RetrieveRenderedInstanceFramesController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/rendered/instances.js b/api/dicom-web/controller/WADO-RS/rendered/instances.js deleted file mode 100644 index e71810c5..00000000 --- a/api/dicom-web/controller/WADO-RS/rendered/instances.js +++ /dev/null @@ -1,61 +0,0 @@ -const _ = require("lodash"); -const mongoose = require("mongoose"); -const renderedService = require("../service/rendered.service"); -const { InstanceImagePathFactory } = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { Controller } = require("../../../../controller.class"); - -class RetrieveRenderedInstancesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - let headerAccept = _.get(this.request.headers, "accept", ""); - - apiLogger.logger.info(`Get study's series' rendered instances, study UID: ${this.request.params.studyUID}, series UID: ${this.request.params.seriesUID}`); - - if (!headerAccept == `multipart/related; type="image/jpeg"`) { - let badRequestMessage = errorResponse.getBadRequestErrorMessage(`header accept only allow \`multipart/related; type="image/jpeg"\`, exception : ${headerAccept}`); - this.response.writeHead(badRequestMessage.HttpStatus, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(badRequestMessage)); - } - - try { - let renderedImageMultipartWriter = new renderedService.RenderedImageMultipartWriter( - this.request, - this.response, - InstanceImagePathFactory, - renderedService.InstanceFramesWriter - ); - - await renderedImageMultipartWriter.write(); - - apiLogger.logger.info(`Write Multipart Successfully, study's series' instances' rendered images, study UID: ${this.request.params.studyUID}, series UID: ${this.request.params.seriesUID}, instance UID: ${this.request.params.instanceUID}`); - return this.response.end(); - } catch(e) { - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify(e)); - } - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - * @returns - */ -module.exports = async function(req, res) { - let controller = new RetrieveRenderedInstancesController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/WADO-RS/rendered/retrieveRendered.controller.js b/api/dicom-web/controller/WADO-RS/rendered/retrieveRendered.controller.js new file mode 100644 index 00000000..62a09856 --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/rendered/retrieveRendered.controller.js @@ -0,0 +1,55 @@ +const _ = require("lodash"); +const renderedService = require("@api/dicom-web/controller/WADO-RS/service/rendered.service"); +const { + StudyImagePathFactory +} = require("@api/dicom-web/controller/WADO-RS/service/WADO-RS.service"); +const errorResponse = require("@root/utils/errorResponse/errorResponseMessage"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { Controller } = require("../../../../controller.class"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseRetrieveRenderedController extends Controller { + /** + * + * @param {import("express").Request} req + * @param {import("express").Response} res + */ + constructor(req, res) { + super(req, res); + } + + async mainProcess() { + let headerAccept = _.get(this.request.headers, "accept", ""); + if (!headerAccept == `multipart/related; type="image/jpeg"`) { + let badRequestMessage = errorResponse.getBadRequestErrorMessage(`header accept only allow \`multipart/related; type="image/jpeg"\`, exception : ${headerAccept}`); + this.response.writeHead(badRequestMessage.HttpStatus, { + "Content-Type": "application/dicom+json" + }); + return this.response.end(JSON.stringify(badRequestMessage)); + } + + try { + + let renderedImageMultipartWriter = new renderedService.RenderedImageMultipartWriter( + this.request, + this.response, + this.request.imagePathFactory, + this.request.framesWriter + ); + + let buffer = await renderedImageMultipartWriter.write(); + + if (buffer instanceof Buffer) { + return this.response.end(buffer, "binary"); + } + + return this.response.end(); + } catch(e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + + +module.exports.BaseRetrieveRenderedController = BaseRetrieveRenderedController; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/rendered/series.js b/api/dicom-web/controller/WADO-RS/rendered/series.js deleted file mode 100644 index bfa027f3..00000000 --- a/api/dicom-web/controller/WADO-RS/rendered/series.js +++ /dev/null @@ -1,56 +0,0 @@ -const mongoose = require("mongoose"); -const _ = require("lodash"); -const renderedService = require("../service/rendered.service"); -const { SeriesImagePathFactory } = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { logger } = require("../../../../../utils/logs/log"); -const { Controller } = require("../../../../controller.class"); - -class RetrieveRenderedSeriesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let headerAccept = _.get(this.request.headers, "accept", ""); - logger.info(`[WADO-RS] [Get study's series' rendered instances, study UID: ${this.request.params.studyUID}, series UID: ${this.request.params.seriesUID}]`); - if (!headerAccept == `multipart/related; type="image/jpeg"`) { - let badRequestMessage = errorResponse.getBadRequestErrorMessage(`header accept only allow \`multipart/related; type="image/jpeg"\`, exception : ${headerAccept}`); - this.response.writeHead(badRequestMessage.HttpStatus, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(badRequestMessage)); - } - - try { - let renderedImageMultipartWriter = new renderedService.RenderedImageMultipartWriter( - this.request, - this.response, - SeriesImagePathFactory, - renderedService.SeriesFramesWriter - ); - - await renderedImageMultipartWriter.write(); - - logger.info(`[WADO-RS] [path: ${this.request.originalUrl}] [Write Multipart Successfully, study's series' rendered instances, study UID: ${this.request.params.studyUID}, series UID: ${this.request.params.seriesUID}]`); - - return this.response.end(); - } catch(e) { - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify(e)); - } - } -} -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - * @returns - */ -module.exports = async function(req, res) { - let controller = new RetrieveRenderedSeriesController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/WADO-RS/rendered/study.js b/api/dicom-web/controller/WADO-RS/rendered/study.js deleted file mode 100644 index 9a8e14f9..00000000 --- a/api/dicom-web/controller/WADO-RS/rendered/study.js +++ /dev/null @@ -1,65 +0,0 @@ -const mongoose = require("mongoose"); -const _ = require("lodash"); -const renderedService = require("../service/rendered.service"); -const { - StudyImagePathFactory -} = require("../service/WADO-RS.service"); -const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { Controller } = require("../../../../controller.class"); - -class RetrieveRenderedStudyController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let headerAccept = _.get(this.request.headers, "accept", ""); - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get study's rendered instances, study UID: ${this.request.params.studyUID}`); - - if (!headerAccept == `multipart/related; type="image/jpeg"`) { - let badRequestMessage = errorResponse.getBadRequestErrorMessage(`header accept only allow \`multipart/related; type="image/jpeg"\`, exception : ${headerAccept}`); - this.response.writeHead(badRequestMessage.HttpStatus, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(badRequestMessage)); - } - - try { - - let renderedImageMultipartWriter = new renderedService.RenderedImageMultipartWriter( - this.request, - this.response, - StudyImagePathFactory, - renderedService.StudyFramesWriter - ); - - await renderedImageMultipartWriter.write(); - - apiLogger.logger.info(`Write Multipart Successfully, study's rendered instances, study UID: ${this.request.params.studyUID}`); - - return this.response.end(); - } catch(e) { - apiLogger.logger.error(e); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify(e)); - } - } -} -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - * @returns - */ -module.exports = async function(req, res) { - let controller = new RetrieveRenderedStudyController(req, res); - - await controller.doPipeline(); -}; diff --git a/api/dicom-web/controller/WADO-RS/retrieveInstance.js b/api/dicom-web/controller/WADO-RS/retrieveInstance.js deleted file mode 100644 index f9f203b1..00000000 --- a/api/dicom-web/controller/WADO-RS/retrieveInstance.js +++ /dev/null @@ -1,93 +0,0 @@ -const wadoService = require("./service/WADO-RS.service"); -const { WADOZip } = require("./service/WADOZip"); -const { ApiLogger } = require("../../../../utils/logs/api-logger"); -const { Controller } = require("../../../controller.class"); -const { RetrieveAuditService } = require("./service/retrieveAudit.service"); -const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); -class RetrieveInstanceOfSeriesOfStudiesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get study's series' instances, study UID: ${this.request.params.studyUID}, series UID: ${this.request.params.seriesUID}`); - apiLogger.logger.info(`Request Accept: ${this.request.headers.accept}`); - - try { - - if (this.request.headers.accept.toLowerCase() === "application/zip") { - return await this.responseZip(); - } else if (this.request.headers.accept.includes("multipart/related")) { - return await this.responseMultipartRelated(); - } else if (this.request.headers.accept.includes("*")){ - this.request.headers.accept = "multipart/related; type=\"application/dicom\""; - return await this.responseMultipartRelated(); - } - - return wadoService.sendNotSupportedMediaType(this.response, this.request.headers.accept); - } catch(e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } - - async responseZip() { - let retrieveAuditService = new RetrieveAuditService(this.request, this.request.params.studyUID, EventOutcomeIndicator.Success); - - let wadoZip = new WADOZip(this.request, this.response); - await retrieveAuditService.onBeginRetrieve(); - - let zipResult = await wadoZip.getZipOfInstanceDICOMFile(); - if (zipResult.status) { - await retrieveAuditService.completedRetrieve(); - return this.response.end(); - } else { - retrieveAuditService.eventResult = EventOutcomeIndicator.MajorFailure; - await retrieveAuditService.completedRetrieve(); - this.response.writeHead(zipResult.code, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(zipResult)); - } - } - - async responseMultipartRelated() { - let type = wadoService.getAcceptType(this.request); - let isSupported = wadoService.supportInstanceMultipartType.indexOf(type) > -1; - if (!isSupported) { - return wadoService.sendNotSupportedMediaType(this.response, type); - } - - let imageMultipartWriter = new wadoService.ImageMultipartWriter( - this.request, - this.response, - wadoService.InstanceImagePathFactory, - wadoService.multipartContentTypeWriter[type] - ); - - return await imageMultipartWriter.write(); - } -} - - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function(req, res) { - let controller = new RetrieveInstanceOfSeriesOfStudiesController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/retrieveInstances.controller.js b/api/dicom-web/controller/WADO-RS/retrieveInstances.controller.js new file mode 100644 index 00000000..95b21456 --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/retrieveInstances.controller.js @@ -0,0 +1,157 @@ +const { Controller } = require("@root/api/controller.class"); +const { RetrieveAuditService } = require("./service/retrieveAudit.service"); +const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); +const { WADOZip } = require("./service/WADOZip"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { + sendNotSupportedMediaType, + getAcceptType, + supportInstanceMultipartType, + ImageMultipartWriter, + InstanceImagePathFactory, + multipartContentTypeWriter, + StudyImagePathFactory, + SeriesImagePathFactory } = require("@api/dicom-web/controller/WADO-RS/service/WADO-RS.service"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseRetrieveController extends Controller { + constructor(req, res) { + super(req, res); + } + + logAction() { + throw new Error("Not implemented."); + } + + async mainProcess() { + try { + if (this.request.headers.accept.toLowerCase() === "application/zip") { + return await this.responseZip(); + } else if (this.request.headers.accept.includes("multipart/related")) { + return await this.responseMultipartRelated(); + } else if (this.request.headers.accept.includes("*")) { + this.request.headers.accept = "multipart/related; type=\"application/dicom\""; + return await this.responseMultipartRelated(); + } + + return sendNotSupportedMediaType(this.response, this.request.headers.accept); + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } + + async responseZip() { + let zipResponseHandler = new this.request.zipResponseHandlerType(this.request, this.response); + await zipResponseHandler.doResponse(); + } + + async responseMultipartRelated() { + let multipartResponseHandler = new this.request.multipartResponseHandlerType(this.request, this.response); + await multipartResponseHandler.doResponse(); + } +} + + +class BaseZipResponseHandler { + constructor(req, res) { + this.request = req; + this.response = res; + this.wadoZip = new WADOZip(this.request, this.response); + this.zipGetter = this.wadoZip.getZipOfInstanceDICOMFile.bind(this.wadoZip); + } + + async doResponse() { + let retrieveAuditService = new RetrieveAuditService(this.request, this.request.params.studyUID, EventOutcomeIndicator.Success); + + await retrieveAuditService.onBeginRetrieve(); + + let zipResult = await this.zipGetter(); + if (zipResult.status) { + await retrieveAuditService.completedRetrieve(); + return this.response.end(); + } else { + retrieveAuditService.eventResult = EventOutcomeIndicator.MajorFailure; + await retrieveAuditService.completedRetrieve(); + this.response.writeHead(zipResult.code, { + "Content-Type": "application/dicom+json" + }); + return this.response.end(JSON.stringify(zipResult)); + } + } +} + +class StudyZipResponseHandler extends BaseZipResponseHandler { + constructor(req, res) { + super(req, res); + this.zipGetter = this.wadoZip.getZipOfStudyDICOMFiles.bind(this.wadoZip); + } +} + +class SeriesZipResponseHandler extends BaseZipResponseHandler { + constructor(req, res) { + super(req, res); + this.zipGetter = this.wadoZip.getZipOfSeriesDICOMFiles.bind(this.wadoZip); + } +} + +class InstanceZipResponseHandler extends BaseZipResponseHandler { + constructor(req, res) { + super(req, res); + this.zipGetter = this.wadoZip.getZipOfInstanceDICOMFile.bind(this.wadoZip); + } +} + +class BaseMultipartRelatedResponseHandler { + constructor(req, res) { + this.request = req; + this.response = res; + this.imagePathFactoryType = StudyImagePathFactory; + } + + async doResponse() { + let type = getAcceptType(this.request); + let isSupported = supportInstanceMultipartType.indexOf(type) > -1; + if (!isSupported) { + return sendNotSupportedMediaType(this.response, type); + } + + let imageMultipartWriter = new ImageMultipartWriter( + this.request, + this.response, + this.imagePathFactoryType, + multipartContentTypeWriter[type] + ); + + return await imageMultipartWriter.write(); + } +} + +class StudyMultipartRelatedResponseHandler extends BaseMultipartRelatedResponseHandler { + constructor(req, res) { + super(req, res); + this.imagePathFactoryType = StudyImagePathFactory; + } +} + +class SeriesMultipartRelatedResponseHandler extends BaseMultipartRelatedResponseHandler { + constructor(req, res) { + super(req, res); + this.imagePathFactoryType = SeriesImagePathFactory; + } +} + +class InstanceMultipartRelatedResponseHandler extends BaseMultipartRelatedResponseHandler { + constructor(req, res) { + super(req, res); + this.imagePathFactoryType = InstanceImagePathFactory; + } +} + +module.exports.BaseRetrieveController = BaseRetrieveController; +module.exports.StudyZipResponseHandler = StudyZipResponseHandler; +module.exports.SeriesZipResponseHandler = SeriesZipResponseHandler; +module.exports.InstanceZipResponseHandler = InstanceZipResponseHandler; +module.exports.StudyMultipartRelatedResponseHandler = StudyMultipartRelatedResponseHandler; +module.exports.SeriesMultipartRelatedResponseHandler = SeriesMultipartRelatedResponseHandler; +module.exports.InstanceMultipartRelatedResponseHandler = InstanceMultipartRelatedResponseHandler; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/retrieveStudy-Series-Instances.js b/api/dicom-web/controller/WADO-RS/retrieveStudy-Series-Instances.js deleted file mode 100644 index 6a998c04..00000000 --- a/api/dicom-web/controller/WADO-RS/retrieveStudy-Series-Instances.js +++ /dev/null @@ -1,90 +0,0 @@ -const { logger } = require("../../../../utils/logs/log"); -const wadoService = require("./service/WADO-RS.service"); -const { WADOZip } = require("./service/WADOZip"); -const errorResponse = require("../../../../utils/errorResponse/errorResponseMessage"); -const { Controller } = require("../../../controller.class"); -const { RetrieveAuditService } = require("./service/retrieveAudit.service"); -const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); - -class RetrieveInstancesOfSeries extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - try { - logger.info(`[WADO-RS] [Get study's series' instances, study UID: ${this.request.params.studyUID}, series UID: ${this.request.params.seriesUID}] [Request Accept: ${this.request.headers.accept}]`); - - if (this.request.headers.accept.toLowerCase() === "application/zip") { - return await this.responseZip(); - } else if (this.request.headers.accept.includes("multipart/related")) { - return await this.responseMultipartRelated(); - } else if (this.request.headers.accept.includes("*")){ - this.request.headers.accept = "multipart/related; type=\"application/dicom\""; - return await this.responseMultipartRelated(); - } - - return wadoService.sendNotSupportedMediaType(this.response, this.request.headers.accept); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - logger.error(`[WADO-RS] [Error: ${errorStr}]`); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } - - async responseZip() { - let retrieveAuditService = new RetrieveAuditService(this.request, this.request.params.studyUID, EventOutcomeIndicator.Success); - - let wadoZip = new WADOZip(this.request, this.response); - await retrieveAuditService.onBeginRetrieve(); - - let zipResult = await wadoZip.getZipOfSeriesDICOMFiles(); - if (zipResult.status) { - await retrieveAuditService.completedRetrieve(); - return this.response.end(); - } else { - retrieveAuditService.eventResult = EventOutcomeIndicator.MajorFailure; - await retrieveAuditService.completedRetrieve(); - - this.response.writeHead(zipResult.code, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(zipResult)); - } - } - - async responseMultipartRelated() { - let type = wadoService.getAcceptType(this.request); - let isSupported = wadoService.supportInstanceMultipartType.indexOf(type) > -1; - if (!isSupported) { - return wadoService.sendNotSupportedMediaType(this.response, type); - } - - let imageMultipartWriter = new wadoService.ImageMultipartWriter( - this.request, - this.response, - wadoService.SeriesImagePathFactory, - wadoService.multipartContentTypeWriter[type] - ); - - return await imageMultipartWriter.write(); - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function (req, res) { - let controller = new RetrieveInstancesOfSeries(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/retrieveStudyInstances.js b/api/dicom-web/controller/WADO-RS/retrieveStudyInstances.js deleted file mode 100644 index 9440669f..00000000 --- a/api/dicom-web/controller/WADO-RS/retrieveStudyInstances.js +++ /dev/null @@ -1,90 +0,0 @@ -const { logger } = require("../../../../utils/logs/log"); -const wadoService = require("./service/WADO-RS.service"); -const { WADOZip } = require("./service/WADOZip"); -const errorResponse = require("../../../../utils/errorResponse/errorResponseMessage"); -const { Controller } = require("../../../controller.class"); -const { RetrieveAuditService } = require("./service/retrieveAudit.service"); -const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); - -class RetrieveStudyInstancesController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - try { - logger.info(`[WADO-RS] [Get study's instances, study UID: ${this.request.params.studyUID}] [Request Accept: ${this.request.headers.accept}]`); - - if (this.request.headers.accept.toLowerCase() === "application/zip") { - return await this.responseZip(); - } else if (this.request.headers.accept.includes("multipart/related")) { - return await this.responseMultipartRelated(); - } else if (this.request.headers.accept.includes("*")) { - this.request.headers.accept = "multipart/related; type=\"application/dicom\""; - return await this.responseMultipartRelated(); - } - - return wadoService.sendNotSupportedMediaType(this.response, this.request.headers.accept); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - logger.error(`[WADO-RS] [Error: ${errorStr}]`); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - this.response.end(JSON.stringify({ - code: 500, - message: errorStr - })); - } - } - - async responseZip() { - let retrieveAuditService = new RetrieveAuditService(this.request, this.request.params.studyUID, EventOutcomeIndicator.Success); - - let wadoZip = new WADOZip(this.request, this.response); - await retrieveAuditService.onBeginRetrieve(); - - let zipResult = await wadoZip.getZipOfStudyDICOMFiles(); - if (zipResult.status) { - await retrieveAuditService.completedRetrieve(); - return this.response.end(); - } else { - retrieveAuditService.eventResult = EventOutcomeIndicator.MajorFailure; - await retrieveAuditService.completedRetrieve(); - - this.response.writeHead(zipResult.code, { - "Content-Type": "application/dicom+json" - }); - return this.response.end(JSON.stringify(zipResult)); - } - } - - async responseMultipartRelated() { - let type = wadoService.getAcceptType(this.request); - let isSupported = wadoService.supportInstanceMultipartType.indexOf(type) > -1; - if (!isSupported) { - return wadoService.sendNotSupportedMediaType(this.response, type); - } - - let imageMultipartWriter = new wadoService.ImageMultipartWriter( - this.request, - this.response, - wadoService.StudyImagePathFactory, - wadoService.multipartContentTypeWriter[type] - ); - - return await imageMultipartWriter.write(); - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function (req, res) { - let controller = new RetrieveStudyInstancesController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/service/WADO-RS.service.js b/api/dicom-web/controller/WADO-RS/service/WADO-RS.service.js index 184b01a7..2cc75361 100644 --- a/api/dicom-web/controller/WADO-RS/service/WADO-RS.service.js +++ b/api/dicom-web/controller/WADO-RS/service/WADO-RS.service.js @@ -1,13 +1,14 @@ const _ = require("lodash"); const path = require("path"); const fsP = require("fs/promises"); -const mongoose = require("mongoose"); const { MultipartWriter } = require("../../../../../utils/multipartWriter"); const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); const { raccoonConfig } = require("../../../../../config-class"); const { JSONPath } = require("jsonpath-plus"); const { DicomWebService } = require("../../../service/dicom-web.service"); -const dicomModel = require("../../../../../models/mongodb/models/dicom"); +const { StudyModel } = require("@dbModels/study.model"); +const { SeriesModel } = require("@dbModels/series.model"); +const { InstanceModel } = require("@dbModels/instance.model"); const { logger } = require("../../../../../utils/logs/log"); const { RetrieveAuditService } = require("./retrieveAudit.service"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); @@ -67,7 +68,7 @@ class ImageMultipartWriter { if (!writeResult.status) { retrieveAuditService.eventResult = EventOutcomeIndicator.MajorFailure; await retrieveAuditService.completedRetrieve(); - + this.response.setHeader("Content-Type", "application/dicom+json"); return this.response.status(writeResult.code).json(writeResult); } @@ -82,10 +83,10 @@ class ImagePathFactory { /** * - * @param {import("../../../../../utils/typeDef/dicom").Uids} uids + * @param {Pick} uids */ constructor(uids) { - /** @type { import("../../../../../utils/typeDef/WADO-RS/WADO-RS.def").ImagePathObj[] } */ + /** @type { import("@root/utils/typeDef/dicomImage").ImagePathObj[] } */ this.imagePaths = []; /** @type {Uids} */ this.uids = uids; @@ -98,7 +99,7 @@ class ImagePathFactory { return { status: false, code: 404, - message: `not found, ${this.getUidsString()}` + message: `not found, ${DicomWebService.getUidsString(this.uids)}` }; } @@ -139,16 +140,6 @@ class ImagePathFactory { return existArr; } - getUidsString() { - let uidsKeys = Object.keys(this.uids); - let strArr = []; - for (let i = 0; i < uidsKeys.length; i++) { - let key = uidsKeys[i]; - strArr.push(`${key}: ${this.uids[key]}`); - } - return strArr.join(", "); - } - getPartialImagesPathString() { return JSON.stringify(this.imagePaths.slice(0, 10).map(v => v.instancePath)); } @@ -160,7 +151,7 @@ class StudyImagePathFactory extends ImagePathFactory { } async getImagePaths() { - this.imagePaths = await mongoose.model("dicomStudy").getPathGroupOfInstances(this.uids); + this.imagePaths = await StudyModel.getPathGroupOfInstances(this.uids); } } @@ -170,7 +161,7 @@ class SeriesImagePathFactory extends ImagePathFactory { } async getImagePaths() { - this.imagePaths = await mongoose.model("dicomSeries").getPathGroupOfInstances(this.uids); + this.imagePaths = await SeriesModel.getPathGroupOfInstances(this.uids); } } @@ -180,9 +171,9 @@ class InstanceImagePathFactory extends ImagePathFactory { } async getImagePaths() { - let imagePath = await dicomModel.getPathOfInstance(this.uids); + let imagePath = await InstanceModel.getPathOfInstance(this.uids); - if(imagePath) + if (imagePath) this.imagePaths = [imagePath]; else this.imagePaths = []; diff --git a/api/dicom-web/controller/WADO-RS/service/WADOZip.js b/api/dicom-web/controller/WADO-RS/service/WADOZip.js index 9e887efa..73a1a9d0 100644 --- a/api/dicom-web/controller/WADO-RS/service/WADOZip.js +++ b/api/dicom-web/controller/WADO-RS/service/WADOZip.js @@ -1,9 +1,10 @@ -const mongoose = require("mongoose"); -const archiver = require("archiver"); -const wadoService = require("./WADO-RS.service"); +const fs = require("fs"); const path = require("path"); -const dicomModel = require("../../../../../models/mongodb/models/dicom"); - +const { StudyModel } = require("@dbModels/study.model"); +const { SeriesModel } = require("@dbModels/series.model"); +const { InstanceModel } = require("@dbModels/instance.model"); +const { SevenZip } = require("@root/utils/sevenZip"); +const { v4: uuidV4 } = require("uuid"); class WADOZip { constructor(iReq, iRes) { this.requestParams = iReq.params; @@ -20,18 +21,15 @@ class WADOZip { } async getZipOfStudyDICOMFiles() { - let imagesPathList = await mongoose.model("dicomStudy").getPathGroupOfInstances(this.requestParams); + let imagesPathList = await StudyModel.getPathGroupOfInstances(this.requestParams); if (imagesPathList.length > 0) { - this.setHeaders(this.studyUID); + await this.#streamZipToResponse(imagesPathList); - let folders = []; - for (let i = 0; i < imagesPathList.length; i++) { - let imagesFolder = path.dirname(imagesPathList[i].instancePath); - if (!folders.includes(imagesFolder)) { - folders.push(imagesFolder); - } - } - return await toZip(this.res, folders); + return { + status: true, + code: 200, + message: "success" + }; } return { status: false, @@ -41,14 +39,14 @@ class WADOZip { } async getZipOfSeriesDICOMFiles() { - let imagesPathList = await mongoose.model("dicomSeries").getPathGroupOfInstances(this.requestParams); + let imagesPathList = await SeriesModel.getPathGroupOfInstances(this.requestParams); if (imagesPathList.length > 0) { - this.setHeaders(this.seriesUID); - - let folders = []; - let seriesPath = path.dirname(imagesPathList[0].instancePath); - folders.push(seriesPath); - return await toZip(this.res, folders); + await this.#streamZipToResponse(imagesPathList); + return { + status: true, + code: 200, + message: "success" + }; } return { status: false, @@ -58,12 +56,14 @@ class WADOZip { } async getZipOfInstanceDICOMFile() { - let imagePath = await dicomModel.getPathOfInstance(this.requestParams); + let imagePath = await InstanceModel.getPathOfInstance(this.requestParams); if (imagePath) { - this.setHeaders(this.instanceUID); - - - return await toZip(this.res, [], imagePath.instancePath); + await this.#streamZipToResponse([imagePath]); + return { + status: true, + code: 200, + message: "success" + }; } return { status: false, @@ -71,40 +71,37 @@ class WADOZip { message: `not found, Instance UID: ${this.requestParams.instanceUID}, Series UID: ${this.requestParams.seriesUID}, Study UID: ${this.requestParams.studyUID}` }; } -} -function toZip(res, folders = [], filename = "") { - return new Promise((resolve) => { - let archive = archiver('zip', { - gzip: true, - zlib: { level: 9 } // Sets the compression level. - }); - archive.on('error', function (err) { - console.error(err); - resolve({ - status: false, - code: 500, - data: err - }); + async #streamZipToResponse(imagesPathList) { + let randomFilename = uuidV4(); + this.setHeaders(randomFilename); + + let zipFile = path.join(__dirname, `../../../../../tempUploadFiles/${randomFilename}.zip`); + await ZipFactory.compressToZipFile(zipFile, imagesPathList); + + fs.createReadStream(zipFile).pipe(this.res); + + this.res.on("finish", () => { + fs.unlink(zipFile, () => { }); }); - archive.pipe(res); - if (folders.length > 0) { - for (let i = 0; i < folders.length; i++) { - let folderName = path.basename(folders[i]); - //archive.append(null, {name : folderName}); - archive.glob("*.dcm", { cwd: folders[i] }, { prefix: folderName }); - } - } else { - archive.file(filename); + } +} + +class ZipFactory { + static async compressToZipFile(zipFile, imagePaths) { + let sevenZip = new SevenZip("zip", undefined, zipFile); + + for (let i = 0; i < imagePaths.length; i++) { + sevenZip.addCmd(imagePaths[i].instancePath); } - archive.finalize().then(() => { - resolve({ - status: true, - code: 200, - data: "success" - }); - }); - }); + + // prevent same filename + if (imagePaths.length >= 2) + sevenZip.useFullyQualifiedFilePaths(); + + sevenZip.overwrite("a"); + await sevenZip.pack(); + } } module.exports.WADOZip = WADOZip; diff --git a/api/dicom-web/controller/WADO-RS/service/metadata.service.js b/api/dicom-web/controller/WADO-RS/service/metadata.service.js new file mode 100644 index 00000000..10321d88 --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/service/metadata.service.js @@ -0,0 +1,39 @@ +const path = require("path"); +const fs = require("fs"); +const fileExist = require("@root/utils/file/fileExist.js"); +const { addHostnameOfBulkDataUrl } = require("./WADO-RS.service.js"); + +class MetadataService { + /** + * @param {import("express").Request} request + * @param {typeof import('./WADO-RS.service.js').ImagePathFactory} imagePathFactory + */ + constructor(request, imagePathFactory) { + this.request = request; + this.imagePathFactory = new imagePathFactory(request.params); + } + + /** + * + * @returns + */ + async getMetadata() { + let metadata = []; + await this.imagePathFactory.getImagePaths(); + for (let imagePathObj of this.imagePathFactory.imagePaths) { + let instanceDir = path.dirname(imagePathObj.instancePath); + let metadataPath = path.join(instanceDir, `${imagePathObj.instanceUID}.metadata.json`); + + if (await fileExist(metadataPath)) { + let metadataJsonStr = fs.readFileSync(metadataPath, { encoding: "utf-8" }); + let metadataJson = JSON.parse(metadataJsonStr); + addHostnameOfBulkDataUrl(metadataJson, this.request); + metadata.push(metadataJson); + } + } + return metadata; + } + +} + +module.exports.MetadataService = MetadataService; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/service/rendered.service.js b/api/dicom-web/controller/WADO-RS/service/rendered.service.js index 4082bce3..0216f40f 100644 --- a/api/dicom-web/controller/WADO-RS/service/rendered.service.js +++ b/api/dicom-web/controller/WADO-RS/service/rendered.service.js @@ -1,103 +1,104 @@ +const _ = require("lodash"); +const imageSizeOf = require("image-size"); +const { promisify } = require("util"); +const imageSizeOfPromise = promisify(imageSizeOf); const path = require("path"); -const mongoose = require("mongoose"); +const { InstanceModel } = require("@dbModels/instance.model"); const fs = require("fs"); -const sharp = require("sharp"); -const _ = require("lodash"); const { Dcm2JpgExecutor } = require("../../../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor"); const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("../../../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions"); const { MultipartWriter } = require("../../../../../utils/multipartWriter"); -const notImageSOPClass = require("../../../../../models/DICOM/dicomWEB/notImageSOPClass"); const Magick = require("../../../../../models/magick"); const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); const { logger } = require("../../../../../utils/logs/log"); const { raccoonConfig } = require("../../../../../config-class"); +const { DicomBulkDataModel } = require("@dbModels/dicomBulkData.model"); -/** - * - * @param {*} param The req.query - * @param {Magick} magick - */ - function handleImageQuality(param, magick) { - if (param.quality) { - magick.quality(param.quality); +class RenderedImageProcessParameterHandler { + #params; + constructor(params) { + this.#params = params; } -} -/** - * - * @param {*} param The req.query - * @param {Magick} magick - * @param {string} instanceID - */ - async function handleImageICCProfile(param, magick, instanceID) { - let iccProfileAction = { - "no" : async ()=> {}, - "yes": async ()=> { - let iccProfileBinaryFile = await mongoose.model("dicomBulkData").findOne({ - $and: [ - { - binaryValuePath: "00480105.Value.0.00282000.InlineBinary" - }, - { - instanceUID: instanceID - } - ] - }); - if(!iccProfileBinaryFile) throw new Error("The Image dose not have icc profile tag"); - let iccProfileSrc = path.join(raccoonConfig.dicomWebConfig.storeRootPath, iccProfileBinaryFile.filename); - let dest = path.join(raccoonConfig.dicomWebConfig.storeRootPath, iccProfileBinaryFile.filename + `.icc`); - if (!fs.existsSync(dest)) fs.copyFileSync(iccProfileSrc, dest); - magick.iccProfile(dest); - }, - "srgb": async ()=> { - magick.iccProfile(path.join(process.cwd(), "models/DICOM/dicomWEB/iccprofiles/sRGB.icc")); - }, - "adobergb": async () => { - magick.iccProfile(path.join(process.cwd(), "models/DICOM/dicomWEB/iccprofiles/adobeRGB.icc")); - }, - "rommrgb": async ()=> { - magick.iccProfile(path.join(process.cwd(), "models/DICOM/dicomWEB/iccprofiles/rommRGB.icc")); - } - }; - try { - if (param.iccprofile) { - await iccProfileAction[param.iccprofile](); + /** + * + * @param {*} param The req.query + * @param {Magick} magick + */ + handleImageQuality(magick) { + if (this.#params?.quality) { + magick.quality(this.#params.quality); } - } catch(e) { - console.error("set icc profile error:" , e); - throw e; } -} -/** - * - * @param {*} param - * @param {sharp.Sharp} imageSharp - * @param {Magick} magick - */ - async function handleViewport(param, imageSharp, magick) { - if (param.viewport) { - let imageMetadata = await imageSharp.metadata(); - let viewportSplit = param.viewport.split(",").map(v => Number(v)); - if (viewportSplit.length == 2) { - let [vw, vh] = viewportSplit; - magick.resize(vw, vh); - } else { - let [vw, vh, sx, sy, sw, sh] = viewportSplit; - magick.resize(vw, vh); - if (sw == 0) sw = imageMetadata.width - sx; - if (sh == 0) sh = imageMetadata.height - sy; - - if (sw < 0) { - magick.flip(); - sw = Math.abs(sw); + /** + * + * @param {Magick} magick + * @param {string} instanceID + */ + async handleImageICCProfile(magick, instanceID) { + let iccProfileAction = { + "no": async () => { }, + "yes": async () => { + let iccProfileBinaryFile = await DicomBulkDataModel.findOneBulkData({ + binaryValuePath: "00480105.Value.0.00282000.InlineBinary", + instanceUID: instanceID + }); + if (!iccProfileBinaryFile) throw new Error("The Image dose not have icc profile tag"); + let iccProfileSrc = path.join(raccoonConfig.dicomWebConfig.storeRootPath, iccProfileBinaryFile.filename); + let dest = path.join(raccoonConfig.dicomWebConfig.storeRootPath, iccProfileBinaryFile.filename + `.icc`); + if (!fs.existsSync(dest)) fs.copyFileSync(iccProfileSrc, dest); + magick.iccProfile(dest); + }, + "srgb": async () => { + magick.iccProfile(path.join(process.cwd(), "models/DICOM/dicomWEB/iccprofiles/sRGB.icc")); + }, + "adobergb": async () => { + magick.iccProfile(path.join(process.cwd(), "models/DICOM/dicomWEB/iccprofiles/adobeRGB.icc")); + }, + "rommrgb": async () => { + magick.iccProfile(path.join(process.cwd(), "models/DICOM/dicomWEB/iccprofiles/rommRGB.icc")); } - if (sh < 0) { - magick.flop(); - sh = Math.abs(sw); + }; + try { + if (this.#params?.iccprofile) { + await iccProfileAction[this.#params.iccprofile](); + } + } catch (e) { + console.error("set icc profile error:", e); + throw e; + } + } + + + /** + * + * @param {{ height: number, width: number }} imageSize + * @param {Magick} magick + */ + async handleViewport(imageSize, magick) { + if (this.#params?.viewport) { + let viewportSplit = this.#params.viewport.split(",").map(v => Number(v)); + if (viewportSplit.length == 2) { + let [vw, vh] = viewportSplit; + magick.resize(vw, vh); + } else { + let [vw, vh, sx, sy, sw, sh] = viewportSplit; + magick.resize(vw, vh); + if (sw == 0) sw = imageSize.width - sx; + if (sh == 0) sh = imageSize.height - sy; + + if (sw < 0) { + magick.flip(); + sw = Math.abs(sw); + } + if (sh < 0) { + magick.flop(); + sh = Math.abs(sw); + } + magick.crop(sx, sy, sw, sh); } - magick.crop(sx, sy, sw, sh); } } } @@ -144,7 +145,7 @@ class RenderedImageMultipartWriter { class FramesWriter { /** * - * @param {import("../../../../../utils/typeDef/WADO-RS/WADO-RS.def").ImagePathObj[]} imagePaths + * @param {import("@root/utils/typeDef/dicomImage").ImagePathObj[]} imagePaths */ constructor(req, res, imagePaths) { this.request = req; @@ -154,9 +155,9 @@ class FramesWriter { async write() { let multipartWriter = new MultipartWriter([], this.request, this.response); - for(let imagePathObj of this.imagePaths) { - let instanceFramesObj = await getInstanceFrameObj(imagePathObj); - if(_.isUndefined(instanceFramesObj)) continue; + for (let imagePathObj of this.imagePaths) { + let instanceFramesObj = await InstanceModel.getInstanceFrame(imagePathObj); + if (_.isUndefined(instanceFramesObj)) continue; let dicomNumberOfFrames = _.get(instanceFramesObj, "00280008.Value.0", 1); dicomNumberOfFrames = parseInt(dicomNumberOfFrames); await writeRenderedImages(this.request, dicomNumberOfFrames, instanceFramesObj, multipartWriter); @@ -168,7 +169,7 @@ class FramesWriter { class StudyFramesWriter extends FramesWriter { /** * - * @param {import("../../../../../utils/typeDef/WADO-RS/WADO-RS.def").ImagePathObj[]} imagePaths + * @param {import("@root/utils/typeDef/dicomImage").ImagePathObj[]} imagePaths */ constructor(req, res, imagePaths) { super(req, res, imagePaths); @@ -188,7 +189,7 @@ class InstanceFramesWriter extends FramesWriter { async write() { let multipartWriter = new MultipartWriter([], this.request, this.response); - let instanceFramesObj = await getInstanceFrameObj(this.imagePaths[0]); + let instanceFramesObj = await InstanceModel.getInstanceFrame(this.imagePaths[0]); if (_.isUndefined(instanceFramesObj)) { return this.response.status(400).json( errorResponse.getBadRequestErrorMessage(`instance: ${this.request.params.instanceUID} doesn't have pixel data`) @@ -209,9 +210,9 @@ class InstanceFramesListWriter extends FramesWriter { } async write() { - let {frameNumber} = this.request.params; + let { frameNumber } = this.request.params; - this.instanceFramesObj = await getInstanceFrameObj(this.imagePaths[0]); + this.instanceFramesObj = await InstanceModel.getInstanceFrame(this.imagePaths[0]); if (_.isUndefined(this.instanceFramesObj)) { return this.response.status(400).json( errorResponse.getBadRequestErrorMessage(`instance: ${this.request.params.instanceUID} doesn't have pixel data`) @@ -226,14 +227,14 @@ class InstanceFramesListWriter extends FramesWriter { return this.writeSingleFrame(); } else { let multipartWriter = new MultipartWriter([], this.request, this.response); - await writeSpecificFramesRenderedImages(this.request, frameNumber, this.instanceFramesObj, multipartWriter); + await writeRenderedImages(this.request, frameNumber, this.instanceFramesObj, multipartWriter); multipartWriter.writeFinalBoundary(); return true; } } isInvalidFrameNumber() { - for(let i = 0; i < this.request.params.frameNumber.length ; i++) { + for (let i = 0; i < this.request.params.frameNumber.length; i++) { let frame = this.request.params.frameNumber[i]; if (frame > this.dicomNumberOfFrames) { let badRequestMessage = errorResponse.getBadRequestErrorMessage(`Bad frame number , \ @@ -258,79 +259,24 @@ This instance NumberOfFrames is : ${this.dicomNumberOfFrames} , But request ${JS this.response.writeHead(200, { "Content-Type": "image/jpeg" }); - + return postProcessResult.magick.toBuffer(); } throw new Error(`Can not process this image, instanceUID: ${this.instanceFramesObj.instanceUID}, frameNumber: ${this.request.frameNumber[0]}`); } } -/** - * - * @param {Object} iParam - * @return { Promise | Promise } - */ -async function getInstanceFrameObj(iParam, otherFields={}) { - let { studyUID, seriesUID, instanceUID } = iParam; - try { - /** @type { import("mongoose").FilterQuery } */ - let query = { - $and: [ - { - studyUID: studyUID - }, - { - seriesUID: seriesUID - }, - { - instanceUID: instanceUID - }, - { - "00080016.Value": { - $nin: notImageSOPClass - } - }, - { - deleteStatus: { - $eq: 0 - } - } - ] - }; - let doc = await mongoose.model("dicom").findOne(query, { - studyUID: 1, - seriesUID: 1, - instanceUID: 1, - instancePath: 1, - "00280008": 1, //number of frames - "00020010": 1, //transfer syntax UID - ...otherFields - }).exec(); - if (doc) { - let docObj = doc.toObject(); - docObj.instancePath = path.join( - raccoonConfig.dicomWebConfig.storeRootPath, - docObj.instancePath - ); - return docObj; - } - return undefined; - } catch (e) { - throw e; - } -} - /** * * @param {import("http").IncomingMessage} req - * @param {import("../../../../../utils/typeDef/WADO-RS/WADO-RS.def").InstanceFrameObj} instanceFramesObj + * @param {import("@root/utils/typeDef/dicomImage").InstanceFrameObj} instanceFramesObj * @returns */ async function postProcessFrameImage(req, frameNumber, instanceFramesObj) { try { let dicomFilename = instanceFramesObj.instancePath; - let jpegFile = dicomFilename.replace(/\.dcm\b/gi , `.${frameNumber-1}.jpg`); + let jpegFile = dicomFilename.replace(/\.dcm\b/gi, `.${frameNumber - 1}.jpg`); let dcm2jpgOptions = await Dcm2JpgExecutor$Dcm2JpgOptions.newInstanceAsync(); dcm2jpgOptions.frameNumber = frameNumber; @@ -341,22 +287,13 @@ async function postProcessFrameImage(req, frameNumber, instanceFramesObj) { ); if (getFrameImageStatus.status) { - let imageSharp = sharp(jpegFile); + let imageSize = await imageSizeOfPromise(jpegFile); let magick = new Magick(jpegFile); - handleImageQuality( - req.query, - magick - ); - await handleImageICCProfile( - req.query, - magick, - instanceFramesObj.instanceUID - ); - await handleViewport( - req.query, - imageSharp, - magick - ); + + let renderedImageProcessParameterHandler = new RenderedImageProcessParameterHandler(req.query); + renderedImageProcessParameterHandler.handleImageQuality(magick); + await renderedImageProcessParameterHandler.handleImageICCProfile(magick, instanceFramesObj.instanceUID); + await renderedImageProcessParameterHandler.handleViewport(imageSize, magick); await magick.execCommand(); return { status: true, @@ -369,7 +306,7 @@ async function postProcessFrameImage(req, frameNumber, instanceFramesObj) { message: "get frame image failed", magick: undefined }; - } catch(e) { + } catch (e) { console.error(e); return { status: false, @@ -382,57 +319,35 @@ async function postProcessFrameImage(req, frameNumber, instanceFramesObj) { /** * * @param {import("express").Request} req - * @param {number} dicomNumberOfFrames - * @param {import("../../../../../utils/typeDef/WADO-RS/WADO-RS.def").ImagePathObj} instanceFramesObj + * @param {number|number[]} dicomNumberOfFrames + * @param {import("@root/utils/typeDef/dicomImage").ImagePathObj} instanceFramesObj * @param {import("../../../../../utils/multipartWriter").MultipartWriter} multipartWriter */ async function writeRenderedImages(req, dicomNumberOfFrames, instanceFramesObj, multipartWriter) { try { - for (let i = 0 ; i < dicomNumberOfFrames; i++) { - let postProcessResult = await postProcessFrameImage(req, i+1, instanceFramesObj); - let buffer = postProcessResult.magick.toBuffer(); - multipartWriter.writeBuffer(buffer, { - "Content-Type": "image/jpeg", - "Content-Location": `/dicom-web/studies/${instanceFramesObj.studyUID}/series/${instanceFramesObj.seriesUID}/instances/${instanceFramesObj.instanceUID}/frames/${i+1}/rendered` - }); - } - } catch(e) { - console.error(e); - throw e; - } -} + // Check if dicomNumberOfFrames is an Array, if it is not an Array then convert it to a 1 to N number Array. + let frames = dicomNumberOfFrames; + if (!Array.isArray(frames)) frames = [...Array(frames).keys()].map(i => i + 1); -/** - * - * @param {import("express").Request} req - * @param {number[]} frames - * @param {import("../../../../../utils/typeDef/WADO-RS/WADO-RS.def").ImagePathObj} instanceFramesObj - * @param {import("../../../../../utils/multipartWriter").MultipartWriter} multipartWriter - */ -async function writeSpecificFramesRenderedImages(req, frames, instanceFramesObj, multipartWriter) { - try { - for (let i = 0 ; i < frames.length; i++) { + for (let i = 0; i < frames.length; i++) { let frameNumber = frames[i]; let postProcessResult = await postProcessFrameImage(req, frameNumber, instanceFramesObj); let buffer = postProcessResult.magick.toBuffer(); multipartWriter.writeBuffer(buffer, { "Content-Type": "image/jpeg", - "Content-Location": `/dicom-web/studies/${instanceFramesObj.studyUID}/series/${instanceFramesObj.seriesUID}/instances/${instanceFramesObj.instanceUID}/frames/${i+1}/rendered` + "Content-Location": `/dicom-web/studies/${instanceFramesObj.studyUID}/series/${instanceFramesObj.seriesUID}/instances/${instanceFramesObj.instanceUID}/frames/${i + 1}/rendered` }); } - } catch(e) { + } catch (e) { console.error(e); throw e; } } -module.exports.handleImageQuality = handleImageQuality; -module.exports.handleImageICCProfile = handleImageICCProfile; -module.exports.handleViewport = handleViewport; -module.exports.getInstanceFrameObj = getInstanceFrameObj; +module.exports.RenderedImageProcessParameterHandler = RenderedImageProcessParameterHandler; +module.exports.getInstanceFrameObj = InstanceModel.getInstanceFrame; module.exports.postProcessFrameImage = postProcessFrameImage; module.exports.writeRenderedImages = writeRenderedImages; -module.exports.writeSpecificFramesRenderedImages = writeSpecificFramesRenderedImages; module.exports.RenderedImageMultipartWriter = RenderedImageMultipartWriter; module.exports.StudyFramesWriter = StudyFramesWriter; module.exports.SeriesFramesWriter = SeriesFramesWriter; diff --git a/api/dicom-web/controller/WADO-RS/service/thumbnail.service.js b/api/dicom-web/controller/WADO-RS/service/thumbnail.service.js index b7f0b47f..9a676e30 100644 --- a/api/dicom-web/controller/WADO-RS/service/thumbnail.service.js +++ b/api/dicom-web/controller/WADO-RS/service/thumbnail.service.js @@ -1,8 +1,9 @@ -const dicomModel = require("../../../../../models/mongodb/models/dicom"); -const dicomSeriesModel = require("../../../../../models/mongodb/models/dicomSeries"); +const { InstanceModel } = require("@dbModels/instance.model"); const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage"); -const renderedService = require("../service/rendered.service"); +const renderedService = require("@api/dicom-web/controller/WADO-RS/service/rendered.service"); const _ = require("lodash"); +const { DicomWebService } = require("@api/dicom-web/service/dicom-web.service"); +const { NotFoundInstanceError } = require("@error/dicom-instance"); class ThumbnailService { /** @@ -18,21 +19,19 @@ class ThumbnailService { this.apiLogger = apiLogger; } - async getThumbnailAndResponse() { + async getThumbnail() { if (!_.get(this.request, "query.viewport")) { _.set(this.request, "query.viewport", "100,100"); } let instanceFramesObj = await this.thumbnailFactory.getThumbnailInstance(); - if (this.checkInstanceExists(instanceFramesObj)) { - return; - } + this.checkInstanceExists(instanceFramesObj); let thumbnail = await this.getThumbnailByInstance(instanceFramesObj); - if (thumbnail) { - return this.response.end(thumbnail, "binary"); + if (!thumbnail) { + throw new Error(`Can not process this image, instanceUID: ${instanceFramesObj.instanceUID}`); } - throw new Error(`Can not process this image, instanceUID: ${instanceFramesObj.instanceUID}`); + return thumbnail; } async getThumbnailByInstance(instanceFramesObj) { @@ -57,41 +56,21 @@ class ThumbnailService { checkInstanceExists(instanceFramesObj) { if (!instanceFramesObj) { - this.response.writeHead(404, { - "Content-Type": "application/dicom+json" - }); - let notFoundMessage = errorResponse.getNotFoundErrorMessage(`Not Found, ${this.thumbnailFactory.getUidsString()}`); - - let notFoundMessageStr = JSON.stringify(notFoundMessage); - - this.apiLogger.logger.warn(`[${notFoundMessageStr}]`); - - return this.response.end(notFoundMessageStr); + throw new NotFoundInstanceError(`Not Found, ${DicomWebService.getUidsString(this.thumbnailFactory.uids)}`); } - return undefined; } } class ThumbnailFactory { /** * - * @param {import("../../../../../utils/typeDef/dicom").Uids} uids + * @param {Pick} uids */ constructor(uids) { this.uids = uids; } async getThumbnailInstance() { } - - getUidsString() { - let uidsKeys = Object.keys(this.uids); - let strArr = []; - for (let i = 0; i < uidsKeys.length; i++) { - let key = uidsKeys[i]; - strArr.push(`${key}: ${this.uids[key]}`); - } - return strArr.join(", "); - } } class StudyThumbnailFactory extends ThumbnailFactory { @@ -101,10 +80,10 @@ class StudyThumbnailFactory extends ThumbnailFactory { /** * - * @param {import("../../../../../utils/typeDef/dicom").Uids} uids + * @param {Pick} uids */ async getThumbnailInstance() { - let medianInstance = await dicomModel.getInstanceOfMedianIndex({ + let medianInstance = await InstanceModel.getInstanceOfMedianIndex({ studyUID: this.uids.studyUID }); if (!medianInstance) return undefined; @@ -127,10 +106,10 @@ class SeriesThumbnailFactory extends ThumbnailFactory { /** * - * @param {import("../../../../../utils/typeDef/dicom").Uids} uids + * @param {Pick} uids */ async getThumbnailInstance() { - let medianInstance = await dicomModel.getInstanceOfMedianIndex({ + let medianInstance = await InstanceModel.getInstanceOfMedianIndex({ studyUID: this.uids.studyUID, seriesUID: this.uids.seriesUID }); @@ -154,7 +133,7 @@ class InstanceThumbnailFactory extends ThumbnailFactory { /** * - * @param {import("../../../../../utils/typeDef/dicom").Uids} uids + * @param {Pick} uids */ async getThumbnailInstance() { let instanceFramesObj = await renderedService.getInstanceFrameObj({ diff --git a/api/dicom-web/controller/WADO-RS/thumbnail/frame.js b/api/dicom-web/controller/WADO-RS/thumbnail/frame.js deleted file mode 100644 index 2354aad0..00000000 --- a/api/dicom-web/controller/WADO-RS/thumbnail/frame.js +++ /dev/null @@ -1,52 +0,0 @@ -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { - ThumbnailService, - InstanceThumbnailFactory -} = require("../service/thumbnail.service"); - - - -class RetrieveFrameThumbnailController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get Study's Series' Instance Thumbnail [study UID: ${this.request.params.studyUID},\ - series UID: ${this.request.params.seriesUID}]\ - instance UID: ${this.request.params.instanceUID}\ - frames: ${JSON.stringify(this.request.params.frameNumber)}`); - - try { - let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, InstanceThumbnailFactory); - return thumbnailService.getThumbnailAndResponse(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end({ - code: 500, - message: "An exception occurred" - }); - } - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function (req, res) { - let controller = new RetrieveFrameThumbnailController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/thumbnail/instance.js b/api/dicom-web/controller/WADO-RS/thumbnail/instance.js deleted file mode 100644 index 12a6a80c..00000000 --- a/api/dicom-web/controller/WADO-RS/thumbnail/instance.js +++ /dev/null @@ -1,51 +0,0 @@ -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { - ThumbnailService, - InstanceThumbnailFactory -} = require("../service/thumbnail.service"); - - - -class RetrieveInstanceThumbnailController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get Study's Series' Instance Thumbnail [study UID: ${this.request.params.studyUID},\ - series UID: ${this.request.params.seriesUID}]\ - instance UID: ${this.request.params.instanceUID}`); - - try { - let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, InstanceThumbnailFactory); - return thumbnailService.getThumbnailAndResponse(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end({ - code: 500, - message: "An exception occurred" - }); - } - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function (req, res) { - let controller = new RetrieveInstanceThumbnailController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/thumbnail/retrieveThumbnail.controller.js b/api/dicom-web/controller/WADO-RS/thumbnail/retrieveThumbnail.controller.js new file mode 100644 index 00000000..e3e50c2b --- /dev/null +++ b/api/dicom-web/controller/WADO-RS/thumbnail/retrieveThumbnail.controller.js @@ -0,0 +1,24 @@ +const { Controller } = require("@root/api/controller.class"); +const { StudyImagePathFactory } = require("@api/dicom-web/controller/WADO-RS/service/WADO-RS.service"); +const { ThumbnailService } = require("../service/thumbnail.service"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); + +class BaseThumbnailController extends Controller { + constructor(req, res) { + super(req, res); + } + + async mainProcess() { + try { + let thumbnailService = new ThumbnailService(this.request, this.response, this.request.logger, this.request.factory); + let thumbnail = await thumbnailService.getThumbnail(); + return this.response.end(thumbnail, "binary"); + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.request.logger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +module.exports.BaseThumbnailController = BaseThumbnailController; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/thumbnail/series.js b/api/dicom-web/controller/WADO-RS/thumbnail/series.js deleted file mode 100644 index 346ca652..00000000 --- a/api/dicom-web/controller/WADO-RS/thumbnail/series.js +++ /dev/null @@ -1,50 +0,0 @@ -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { - ThumbnailService, - SeriesThumbnailFactory -} = require("../service/thumbnail.service"); - - - -class RetrieveSeriesThumbnailController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get Study's Series' Thumbnail [study UID: ${this.request.params.studyUID},\ - series UID: ${this.request.params.seriesUID}]`); - - try { - let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, SeriesThumbnailFactory); - return thumbnailService.getThumbnailAndResponse(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end({ - code: 500, - message: "An exception occurred" - }); - } - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function (req, res) { - let controller = new RetrieveSeriesThumbnailController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/controller/WADO-RS/thumbnail/study.js b/api/dicom-web/controller/WADO-RS/thumbnail/study.js deleted file mode 100644 index c9e3899f..00000000 --- a/api/dicom-web/controller/WADO-RS/thumbnail/study.js +++ /dev/null @@ -1,49 +0,0 @@ -const { Controller } = require("../../../../controller.class"); -const { ApiLogger } = require("../../../../../utils/logs/api-logger"); -const { - ThumbnailService, - StudyThumbnailFactory -} = require("../service/thumbnail.service"); - - - -class RetrieveStudyThumbnailController extends Controller { - constructor(req, res) { - super(req, res); - } - - async mainProcess() { - - let apiLogger = new ApiLogger(this.request, "WADO-RS"); - apiLogger.addTokenValue(); - - apiLogger.logger.info(`Get Study's Thumbnail [study UID: ${this.request.params.studyUID}]`); - - try { - let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, StudyThumbnailFactory); - return thumbnailService.getThumbnailAndResponse(); - } catch (e) { - let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); - apiLogger.logger.error(errorStr); - - this.response.writeHead(500, { - "Content-Type": "application/dicom+json" - }); - return this.response.end({ - code: 500, - message: "An exception occurred" - }); - } - } -} - -/** - * - * @param {import("http").IncomingMessage} req - * @param {import("http").ServerResponse} res - */ -module.exports = async function (req, res) { - let controller = new RetrieveStudyThumbnailController(req, res); - - await controller.doPipeline(); -}; \ No newline at end of file diff --git a/api/dicom-web/delete.route.js b/api/dicom-web/delete.route.js index 0c6e1242..65ffe630 100644 --- a/api/dicom-web/delete.route.js +++ b/api/dicom-web/delete.route.js @@ -3,14 +3,50 @@ const Joi = require("joi"); const { validateParams, intArrayJoi } = require("../validator"); const router = express(); +const { BaseDeleteController } = require("./controller/WADO-RS/deletion/delete.controller"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const DeleteController = async (req, res) => { + let deleteController = new BaseDeleteController(req, res); + await deleteController.doPipeline(); +}; //#region Delete -router.delete("/studies/:studyUID", require("./controller/WADO-RS/deletion/study")); +router.delete( + "/studies/:studyUID", + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`delete study: ${req.params.studyUID}`); + req.dicomLevel = "study"; + next(); + }, + DeleteController +); -router.delete("/studies/:studyUID/series/:seriesUID", require("./controller/WADO-RS/deletion/series")); +router.delete( + "/studies/:studyUID/series/:seriesUID", + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`delete series, study: ${req.params.studyUID}, series: ${req.params.seriesUID}`); + req.dicomLevel = "series"; + next(); + }, + DeleteController +); -router.delete("/studies/:studyUID/series/:seriesUID/instances/:instanceUID", require("./controller/WADO-RS/deletion/instance")); +router.delete( + "/studies/:studyUID/series/:seriesUID/instances/:instanceUID", + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`delete instance, study: ${req.params.studyUID}, series: ${req.params.seriesUID}, instance: ${req.params.instanceUID}`); + req.dicomLevel = "instance"; + next(); + }, + DeleteController +); //#endregion diff --git a/api/dicom-web/mwl-rs.route.js b/api/dicom-web/mwl-rs.route.js new file mode 100644 index 00000000..a955f071 --- /dev/null +++ b/api/dicom-web/mwl-rs.route.js @@ -0,0 +1,149 @@ +const express = require("express"); +const Joi = require("joi"); +const { validateParams, intArrayJoi, validateByJoi } = require("@root/api/validator"); +const router = express(); + + +/** + * @openapi + * /dicom-web/mwlitems: + * post: + * tags: + * - MWL-RS + * description: > + * This transaction create or update a Modality WorkList item. + * responses: + * "200": + * description: The workitem create successfully + */ +router.post("/mwlitems", + validateByJoi(Joi.array().items( + Joi.object({ + "00100020": Joi.object({ + vr: Joi.string().valid("LO").required(), + Value: Joi.array().items(Joi.string()).min(1).max(1).required() + }).required(), + "00400100": Joi.object({ + vr: Joi.string().valid("SQ").required(), + Value: Joi.array().items(Joi.object()).min(1).required() + }).required() + }) + ).min(1).max(1), "body", {allowUnknown: true}), + require("./controller/MWL-RS/create-mwlItem") +); + +/** + * @openapi + * /dicom-web/mwlitems: + * get: + * tags: + * - MWL-RS + * description: > + * This transaction search Modality WorkList items. + * parameters: + * - $ref: "#/components/parameters/filter" + * responses: + * "200": + * description: Query successfully + * content: + * "application/dicom+json": + * schema: + * type: array + * items: + * type: object + */ +router.get("/mwlitems",require("./controller/MWL-RS/get-mwlItem")); + +/** + * @openapi + * /dicom-web/mwlitems/count: + * get: + * tags: + * - MWL-RS + * description: > + * This transaction get Modality WorkList items count. + * parameters: + * - $ref: "#/components/parameters/filter" + * responses: + * "200": + * description: Query successfully + * content: + * "application/dicom+json": + * schema: + * properties: + * count: + * type: number + */ +router.get("/mwlitems/count",require("./controller/MWL-RS/count-mwlItem")); + +/** + * @openapi + * /dicom-web/mwlitems/{studyUID}/{spsID}: + * delete: + * tags: + * - MWL-RS + * description: > + * This transaction deletes a Modality WorkList item. + * parameters: + * - $ref: "#/components/parameters/studyUID" + * - $ref: "#/components/parameters/spsID" + * responses: + * "204": + * description: Delete successfully + */ +router.delete("/mwlitems/:studyUID/:spsID", validateParams({ + studyUID: Joi.string().required(), + spsID: Joi.string().required() +}, "params", undefined), require("./controller/MWL-RS/delete-mwlItem")); + + +/** + * @openapi + * /dicom-web/mwlitems/{studyUID}/{spsID}/status/{spsStatus}: + * post: + * tags: + * - MWL-RS + * description: > + * This transaction create or update a Modality WorkList item. + * parameters: + * - $ref: "#/components/parameters/studyUID" + * - $ref: "#/components/parameters/spsID" + * - $ref: "#/components/parameters/spsStatus" + * responses: + * "200": + * description: change status of mwl item successfully + */ +router.post("/mwlitems/:studyUID/:spsID/status/:status", + validateByJoi( + Joi.object({ + studyUID: Joi.string().required(), + spsID: Joi.string().required(), + status: Joi.string().valid("SCHEDULED", "ARRIVED", "READY", "STARTED", "DEPARTED", "CANCELED", "DISCONTINUED", "COMPLETED").required() + }), "params", {allowUnknown: false}), + require("./controller/MWL-RS/change-mwlItem-status") +); + +/** + * @openapi + * /dicom-web/mwlitems/status/{spsStatus}: + * post: + * tags: + * - MWL-RS + * description: > + * This transaction create or update a Modality WorkList item. + * parameters: + * - $ref: "#/components/parameters/spsStatus" + * - $ref: "#/components/parameters/filter" + * responses: + * "200": + * description: change status of mwl items successfully + */ +router.post("/mwlitems/status/:status", + validateByJoi( + Joi.object({ + status: Joi.string().valid("SCHEDULED", "ARRIVED", "READY", "STARTED", "DEPARTED", "CANCELED", "DISCONTINUED", "COMPLETED").required() + }), "params", {allowUnknown: false}), + require("./controller/MWL-RS/change-filtered-mwlItem-status") +); + +module.exports = router; \ No newline at end of file diff --git a/api/dicom-web/pam-rs.route.js b/api/dicom-web/pam-rs.route.js new file mode 100644 index 00000000..0c64c85c --- /dev/null +++ b/api/dicom-web/pam-rs.route.js @@ -0,0 +1,65 @@ +const express = require("express"); +const router = express(); +const Joi = require("joi"); +const { validateParams } = require("../validator"); + + +/** + * @openapi + * /dicom-web/patients: + * post: + * tags: + * - PAM-RS + * description: Create new patient + * responses: + * 200: + * description: Create patient successfully + * content: + * "application/dicom+json": + * schema: + * type: object + * properties: + * patientID: + * type: string + * + */ +router.post("/patients", validateParams({ + "00100020": Joi.object({ + "vr": Joi.string().required().allow("LO"), + "Value": Joi.array().items(Joi.string()).required().min(1) + }).required() +}, "body", { allowUnknown: true }), require("./controller/PAM-RS/create-patient")); + + +/** + * @openapi + * /dicom-web/patients/{patientID}: + * put: + * tags: + * - PAM-RS + * description: Create new patient + * parameters: + * - $ref: "#/components/parameters/patientID" + * requestBody: + * required: true + * content: + * "application/dicom+json": + * schema: + * $ref: "#/components/schemas/PatientRequiredMatchingAttributes" + * "application/json": + * schema: + * $ref: "#/components/schemas/PatientRequiredMatchingAttributes" + * responses: + * 200: + * description: Create patient successfully + * content: + * "application/dicom+json": + * schema: + * $ref: "#/components/schemas/PatientRequiredMatchingAttributes" + * + */ +router.put("/patients/:patientID", require("./controller/PAM-RS/update-patient")); + +router.delete("/patients/:patientID", require("./controller/PAM-RS/delete-patient")); + +module.exports = router; \ No newline at end of file diff --git a/api/dicom-web/qido-rs.route.js b/api/dicom-web/qido-rs.route.js index 5fc87490..c1930e1d 100644 --- a/api/dicom-web/qido-rs.route.js +++ b/api/dicom-web/qido-rs.route.js @@ -5,6 +5,8 @@ const router = express(); const { dictionary } = require("../../models/DICOM/dicom-tags-dic"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { BaseQueryController } = require("./controller/QIDO-RS/base.controller"); const KEYWORD_KEYS = Object.keys(dictionary.keyword); const HEX_KEYS = Object.keys(dictionary.tag); @@ -22,7 +24,8 @@ const queryValidation = { return convertKeywordToHex(attribute); } ) - ).single() + ).single(), + isRecycle: Joi.boolean().default(false) }; /** @@ -47,6 +50,10 @@ function convertKeywordToHex(attribute) { return attribute; } +const queryController = async (req, res) => { + let controller = new BaseQueryController(req, res); + await controller.doPipeline(); +}; /** * @openapi @@ -64,6 +71,7 @@ function convertKeywordToHex(attribute) { * - $ref: "#/components/parameters/PatientName" * - $ref: "#/components/parameters/PatientID" * - $ref: "#/components/parameters/StudyID" + * - $ref: "#/components/parameters/isRecycle" * responses: * 200: * description: Query successfully @@ -77,7 +85,13 @@ function convertKeywordToHex(attribute) { */ router.get("/studies", validateParams(queryValidation, "query", { allowUnknown: true -}), require("./controller/QIDO-RS/queryAllStudies")); +}), (req, res, next) => { + req.dicomLevel = "study"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Query Studies, Incoming Parameters: ${JSON.stringify(req.query)}`); + next(); +}, queryController); /** * @openapi @@ -98,6 +112,7 @@ router.get("/studies", validateParams(queryValidation, "query", { * - $ref: "#/components/parameters/StudyID" * - $ref: "#/components/parameters/Modality" * - $ref: "#/components/parameters/SeriesNumber" + * - $ref: "#/components/parameters/isRecycle" * responses: * 200: * description: Query successfully @@ -114,7 +129,14 @@ router.get( "/studies/:studyUID/series", validateParams(queryValidation, "query", { allowUnknown: true }), - require("./controller/QIDO-RS/queryStudies-Series") + (req, res, next) => { + req.dicomLevel = "series"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info("Query Study's Series, Incoming Parameters: " + JSON.stringify(req.query)); + next(); + }, + queryController ); /** @@ -138,6 +160,7 @@ router.get( * - $ref: "#/components/parameters/SeriesNumber" * - $ref: "#/components/parameters/SOPClassUID" * - $ref: "#/components/parameters/InstanceNumber" + * - $ref: "#/components/parameters/isRecycle" * responses: * 200: * description: Query successfully @@ -155,7 +178,14 @@ router.get( "/studies/:studyUID/instances", validateParams(queryValidation, "query", { allowUnknown: true }), - require("./controller/QIDO-RS/queryStudies-Instances") + (req, res, next) => { + req.dicomLevel = "instance"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info("Query Study's Instances, Incoming Parameters: " + JSON.stringify(req.query)); + next(); + }, + queryController ); /** @@ -180,6 +210,7 @@ router.get( * - $ref: "#/components/parameters/SeriesNumber" * - $ref: "#/components/parameters/SOPClassUID" * - $ref: "#/components/parameters/InstanceNumber" + * - $ref: "#/components/parameters/isRecycle" * responses: * 200: * description: Query successfully @@ -197,7 +228,14 @@ router.get( "/studies/:studyUID/series/:seriesUID/instances", validateParams(queryValidation, "query", { allowUnknown: true }), - require("./controller/QIDO-RS/queryStudies-Series-Instance") + (req, res, next) => { + req.dicomLevel = "instance"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info("Query Study's Series' Instances, Incoming Parameters: " + JSON.stringify(req.query)); + next(); + }, + queryController ); /** @@ -218,6 +256,7 @@ router.get( * - $ref: "#/components/parameters/StudyID" * - $ref: "#/components/parameters/Modality" * - $ref: "#/components/parameters/SeriesNumber" + * - $ref: "#/components/parameters/isRecycle" * responses: * 200: * description: Query successfully @@ -235,7 +274,14 @@ router.get( "/series", validateParams(queryValidation, "query", { allowUnknown: true }), - require("./controller/QIDO-RS/queryAllSeries") + (req, res, next) => { + req.dicomLevel = "series"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info("Query All Series, Incoming Parameters: " + JSON.stringify(req.query)); + next(); + }, + queryController ); /** @@ -258,6 +304,7 @@ router.get( * - $ref: "#/components/parameters/SeriesNumber" * - $ref: "#/components/parameters/SOPClassUID" * - $ref: "#/components/parameters/InstanceNumber" + * - $ref: "#/components/parameters/isRecycle" * responses: * 200: * description: Query successfully @@ -275,7 +322,14 @@ router.get( "/instances", validateParams(queryValidation, "query", { allowUnknown: true }), - require("./controller/QIDO-RS/queryAllInstances") + (req, res, next) => { + req.dicomLevel = "instance"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info("Query All Instances, Incoming Parameters: " + JSON.stringify(req.query)); + next(); + }, + queryController ); /** @@ -303,7 +357,17 @@ router.get( */ router.get( "/patients", - require("./controller/QIDO-RS/allPatient") + validateParams({limit: queryValidation.limit, offset: queryValidation.offset}, "query", { + allowUnknown: true + }), + (req, res, next) => { + req.dicomLevel = "patient"; + req.logger = new ApiLogger(req, "QIDO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info("Query All Patients, Incoming Parameters: " + JSON.stringify(req.query)); + next(); + }, + queryController ); //#endregion diff --git a/api/dicom-web/service/base-query.service.js b/api/dicom-web/service/base-query.service.js new file mode 100644 index 00000000..3a023e45 --- /dev/null +++ b/api/dicom-web/service/base-query.service.js @@ -0,0 +1,100 @@ +const _ = require("lodash"); +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); + + +class BaseQueryService { + constructor(req, res) { + this.request = req; + this.response = res; + + this.query = {}; + + /** + * @private + */ + this.limit_ = parseInt(this.request.query.limit) || 100; + delete this.request.query["limit"]; + + /** + * @private + */ + this.skip_ = parseInt(this.request.query.offset) || 0; + delete this.request.query["offset"]; + + /** + * @private + */ + this.includeFields_ = this.request.query["includefield"] || []; + + if (this.includeFields_.includes("all")) { + this.includeFields_ = ["all"]; + } + + delete this.request.query["includefield"]; + + this.initQuery_(); + } + + /** + * @private + */ + initQuery_() { + let query = _.cloneDeep(this.request.query); + let queryKeys = Object.keys(query).sort(); + for (let i = 0; i < queryKeys.length; i++) { + let queryKey = queryKeys[i]; + if (!query[queryKey]) delete query[queryKey]; + } + + this.query = convertAllQueryToDicomTag(query); + } +} + +/** + * Convert All of name(tags, keyword) of queries to tags number + * @param {Object} iParam The request query. + * @returns + */ +function convertAllQueryToDicomTag(iParam, pushSuffixValue=true) { + let keys = Object.keys(iParam); + let newQS = {}; + for (let i = 0; i < keys.length; i++) { + let keyName = keys[i]; + let keyNameSplit = keyName.split("."); + let newKeyNames = []; + for (let x = 0; x < keyNameSplit.length; x++) { + if (dictionary.keyword[keyNameSplit[x]]) { + newKeyNames.push(dictionary.keyword[keyNameSplit[x]]); + } else if (dictionary.tag[keyNameSplit[x]]) { + newKeyNames.push(keyNameSplit[x]); + } + } + let retKeyName; + if (newKeyNames.length === 0) { + throw new DicomWebServiceError( + DicomWebStatusCodes.InvalidArgumentValue, + `Invalid request query: ${keyNameSplit}`, + 400 + ); + } + + if (pushSuffixValue) { + if (newKeyNames.length >= 2) { + retKeyName = newKeyNames.map(v => v + ".Value").join("."); + } else { + newKeyNames.push("Value"); + retKeyName = newKeyNames.join("."); + } + } else { + retKeyName = newKeyNames.join("."); + } + + newQS[retKeyName] = iParam[keyName]; + } + return newQS; +} +//#endregion + +module.exports.BaseQueryService = BaseQueryService; +module.exports.convertAllQueryToDicomTag = convertAllQueryToDicomTag; \ No newline at end of file diff --git a/api/dicom-web/service/dicom-web.service.js b/api/dicom-web/service/dicom-web.service.js index e391d996..225f311d 100644 --- a/api/dicom-web/service/dicom-web.service.js +++ b/api/dicom-web/service/dicom-web.service.js @@ -14,7 +14,7 @@ class DicomWebService { this.protocol = req.secure ? "https" : "http"; } - + getBasicURL() { let hostname = raccoonConfig.dicomWebConfig.host; @@ -53,6 +53,21 @@ class DicomWebService { static getServerHostname() { return `${raccoonConfig.serverConfig.host}`; } + + /** + * + * @param {Pick} uids + * @returns + */ + static getUidsString(uids) { + let uidsKeys = Object.keys(uids); + let strArr = []; + for (let i = 0; i < uidsKeys.length; i++) { + let key = uidsKeys[i]; + strArr.push(`${key}: ${uids[key]}`); + } + return strArr.join(", "); + } } module.exports.DicomWebService = DicomWebService; \ No newline at end of file diff --git a/api/dicom-web/ups-rs.route.js b/api/dicom-web/ups-rs.route.js index 7e08fa56..a04a0b40 100644 --- a/api/dicom-web/ups-rs.route.js +++ b/api/dicom-web/ups-rs.route.js @@ -53,6 +53,8 @@ router.post("/workitems", * "application/dicom+json": * schema: * type: array + * items: + * type: object */ router.get("/workitems", require("./controller/UPS-RS/get-workItem") @@ -76,6 +78,8 @@ router.get("/workitems", * "application/dicom+json": * schema: * type: array + * items: + * type: object */ router.get("/workitems/:workItem", require("./controller/UPS-RS/get-workItem") diff --git a/api/dicom-web/wado-rs-bulkdata.route.js b/api/dicom-web/wado-rs-bulkdata.route.js index 79e76e5e..34d0d022 100644 --- a/api/dicom-web/wado-rs-bulkdata.route.js +++ b/api/dicom-web/wado-rs-bulkdata.route.js @@ -3,6 +3,16 @@ const Joi = require("joi"); const { validateParams, intArrayJoi } = require("../validator"); const router = express(); +const { BaseBulkDataController } = require("./controller/WADO-RS/bulkdata/retrieveBulkData.controller"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StudyBulkDataFactory, SeriesBulkDataFactory, InstanceBulkDataFactory, SpecificBulkDataFactory } = require("./controller/WADO-RS/bulkdata/service/bulkdata"); +const { StudyImagePathFactory, SeriesImagePathFactory, InstanceImagePathFactory } = require("./controller/WADO-RS/service/WADO-RS.service"); + +const BulkDataController = async (req, res) => { + let bulkDataController = new BaseBulkDataController(req, res); + await bulkDataController.doPipeline(); +}; + /** * @openapi * /dicom-web/studies/{studyUID}/bulkdata: @@ -19,7 +29,15 @@ const router = express(); */ router.get( "/studies/:studyUID/bulkdata", - require("./controller/WADO-RS/bulkdata/study") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get bulk data from study: ${req.params.studyUID}`); + req.bulkDataFactoryType = StudyBulkDataFactory; + req.imagePathFactoryType = StudyImagePathFactory; + next(); + }, + BulkDataController ); /** @@ -39,7 +57,15 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/bulkdata", - require("./controller/WADO-RS/bulkdata/series") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get series' bulk data from study: ${req.params.studyUID}, series: ${req.params.seriesUID}`); + req.bulkDataFactoryType = SeriesBulkDataFactory; + req.imagePathFactoryType = SeriesImagePathFactory; + next(); + }, + BulkDataController ); /** @@ -60,7 +86,15 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID/bulkdata", - require("./controller/WADO-RS/bulkdata/instance") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get instance's bulk data from study: ${req.params.studyUID}, series: ${req.params.seriesUID}, instance: ${req.params.instanceUID}`); + req.bulkDataFactoryType = InstanceBulkDataFactory; + req.imagePathFactoryType = InstanceImagePathFactory; + next(); + }, + BulkDataController ); /** @@ -82,7 +116,16 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID/bulkdata/:binaryValuePath", - require("./controller/WADO-RS/bulkdata/bulkdata") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get bulk data from study: ${req.params.studyUID}, series: ${req.params.seriesUID}, instance: ${req.params.instanceUID}`+ + `, binaryValuePath: ${req.params.binaryValuePath}`); + req.bulkDataFactoryType = SpecificBulkDataFactory; + req.imagePathFactoryType = StudyImagePathFactory; + next(); + }, + BulkDataController ); module.exports = router; \ No newline at end of file diff --git a/api/dicom-web/wado-rs-instance.route.js b/api/dicom-web/wado-rs-instance.route.js index bdcb4ee3..a2995c94 100644 --- a/api/dicom-web/wado-rs-instance.route.js +++ b/api/dicom-web/wado-rs-instance.route.js @@ -3,6 +3,23 @@ const Joi = require("joi"); const { validateParams, intArrayJoi } = require("../validator"); const router = express(); + +const { + BaseRetrieveController, + StudyZipResponseHandler, + StudyMultipartRelatedResponseHandler, + SeriesZipResponseHandler, + SeriesMultipartRelatedResponseHandler, + InstanceZipResponseHandler, + InstanceMultipartRelatedResponseHandler +} = require("./controller/WADO-RS/retrieveInstances.controller"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); + +let retrieveController = async (req, res) => { + let controller = new BaseRetrieveController(req, res); + await controller.doPipeline(); +}; + //#region WADO-RS Retrieve Transaction Instance Resources /** @@ -21,7 +38,15 @@ const router = express(); */ router.get( "/studies/:studyUID", - require("./controller/WADO-RS/retrieveStudyInstances") + (req, res, next) => { + req.zipResponseHandlerType = StudyZipResponseHandler; + req.multipartResponseHandlerType = StudyMultipartRelatedResponseHandler; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`[Get study's instances, study UID: ${req.params.studyUID}] [Request Accept: ${req.headers?.accept}]`); + next(); + }, + retrieveController ); /** @@ -41,7 +66,15 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID", - require("./controller/WADO-RS/retrieveStudy-Series-Instances") + (req, res, next) => { + req.zipResponseHandlerType = SeriesZipResponseHandler; + req.multipartResponseHandlerType = SeriesMultipartRelatedResponseHandler; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`[Get study's series' instances, study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}] [Request Accept: ${req.headers?.accept}]`); + next(); + }, + retrieveController ); /** @@ -62,7 +95,18 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID", - require("./controller/WADO-RS/retrieveInstance") + (req, res, next) => { + req.zipResponseHandlerType = InstanceZipResponseHandler; + req.multipartResponseHandlerType = InstanceMultipartRelatedResponseHandler; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`[Get study's series' instance,`+ + `study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}, instance UID: ${req.params.instanceUID}]` + + ` [Request Accept: ${req.headers?.accept}]`); + + next(); + }, + retrieveController ); //#endregion diff --git a/api/dicom-web/wado-rs-metadata.route.js b/api/dicom-web/wado-rs-metadata.route.js index 59f15e51..acff3ecb 100644 --- a/api/dicom-web/wado-rs-metadata.route.js +++ b/api/dicom-web/wado-rs-metadata.route.js @@ -3,6 +3,13 @@ const Joi = require("joi"); const { validateParams, intArrayJoi } = require("../validator"); const router = express(); +const { BaseRetrieveMetadataController } = require("./controller/WADO-RS/metadata/retrieveMetadata.controller"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StudyImagePathFactory, SeriesImagePathFactory, InstanceImagePathFactory } = require("./controller/WADO-RS/service/WADO-RS.service"); +const RetrieveMetadataController = async (req, res) => { + let controller = new BaseRetrieveMetadataController(req, res); + await controller.doPipeline(); +}; //#region WADO-RS Retrieve Transaction Metadata Resources @@ -22,7 +29,15 @@ const router = express(); */ router.get( "/studies/:studyUID/metadata", - require("./controller/WADO-RS/metadata/retrieveStudyMetadata") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(); + req.logger.logger.info(`Get study's metadata, study UID: ${req.params.studyUID}`); + req.imagePathFactory = StudyImagePathFactory; + next(); + }, + RetrieveMetadataController ); /** @@ -42,7 +57,15 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/metadata", - require("./controller/WADO-RS/metadata/retrieveSeriesMetadata") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(); + req.logger.logger.info(`Get study's series's metadata, study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}`); + req.imagePathFactory = SeriesImagePathFactory; + next(); + }, + RetrieveMetadataController ); /** @@ -63,7 +86,15 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID/metadata", - require("./controller/WADO-RS/metadata/retrieveInstanceMetadata") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get instance's metadata, study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID},`+ + ` instance UID: ${req.params.instanceUID}`); + req.imagePathFactory = InstanceImagePathFactory; + next(); + }, + RetrieveMetadataController ); //#endregion diff --git a/api/dicom-web/wado-rs-rendered.route.js b/api/dicom-web/wado-rs-rendered.route.js index 6e07187d..726c33cf 100644 --- a/api/dicom-web/wado-rs-rendered.route.js +++ b/api/dicom-web/wado-rs-rendered.route.js @@ -3,6 +3,14 @@ const Joi = require("joi"); const { validateParams, intArrayJoi } = require("../validator"); const router = express(); +const { BaseRetrieveRenderedController } = require("./controller/WADO-RS/rendered/retrieveRendered.controller"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StudyImagePathFactory, SeriesImagePathFactory, InstanceImagePathFactory } = require("./controller/WADO-RS/service/WADO-RS.service"); +const { StudyFramesWriter, SeriesFramesWriter, InstanceFramesWriter, InstanceFramesListWriter } = require("./controller/WADO-RS/service/rendered.service"); +const RetrieveRenderedController = async (req, res) => { + let controller = new BaseRetrieveRenderedController(req, res); + await controller.doPipeline(); +}; //#region WADO-RS Retrieve Transaction Rendered Resources @@ -60,7 +68,16 @@ const renderedQueryValidation = { router.get( "/studies/:studyUID/rendered", validateParams(renderedQueryValidation, "query", { allowUnknown: false }), - require("./controller/WADO-RS/rendered/study") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(); + req.logger.logger.info(`Get study's rendered images, study UID: ${req.params.studyUID}`); + req.imagePathFactory = StudyImagePathFactory; + req.framesWriter = StudyFramesWriter; + next(); + }, + RetrieveRenderedController ); /** @@ -84,7 +101,16 @@ router.get( router.get( "/studies/:studyUID/series/:seriesUID/rendered", validateParams(renderedQueryValidation, "query", { allowUnknown: false }), - require("./controller/WADO-RS/rendered/series") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(); + req.logger.logger.info(`Get study's series' rendered images, study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}`); + req.imagePathFactory = SeriesImagePathFactory; + req.framesWriter = SeriesFramesWriter; + next(); + }, + RetrieveRenderedController ); /** @@ -109,7 +135,16 @@ router.get( router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID/rendered", validateParams(renderedQueryValidation, "query", { allowUnknown: false }), - require("./controller/WADO-RS/rendered/instances") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get study's series' instance rendered images, study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}`+ + `, instance UID: ${req.params.instanceUID}`); + req.imagePathFactory = InstanceImagePathFactory; + req.framesWriter = InstanceFramesWriter; + next(); + }, + RetrieveRenderedController ); /** @@ -138,7 +173,16 @@ router.get( frameNumber : intArrayJoi.intArray().items(Joi.number().integer().min(1)).single() } , "params" , {allowUnknown : true}), validateParams(renderedQueryValidation, "query", { allowUnknown: false }), - require("./controller/WADO-RS/rendered/instanceFrames") + (req, res, next) => { + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get instance' rendered frames, study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}`+ + `, instance UID: ${req.params.instanceUID}, frame numbers: ${req.params.frameNumber}`); + req.imagePathFactory = InstanceImagePathFactory; + req.framesWriter = InstanceFramesListWriter; + next(); + }, + RetrieveRenderedController ); //#endregion diff --git a/api/dicom-web/wado-rs-thumbnail.route.js b/api/dicom-web/wado-rs-thumbnail.route.js index 9b9e6c4c..d33f2edb 100644 --- a/api/dicom-web/wado-rs-thumbnail.route.js +++ b/api/dicom-web/wado-rs-thumbnail.route.js @@ -3,6 +3,14 @@ const Joi = require("joi"); const { validateParams, intArrayJoi } = require("../validator"); const router = express(); +const { BaseThumbnailController } = require("./controller/WADO-RS/thumbnail/retrieveThumbnail.controller"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StudyThumbnailFactory, SeriesThumbnailFactory, InstanceThumbnailFactory } = require("./controller/WADO-RS/service/thumbnail.service"); + +const RetrieveThumbnailController = async function (req, res) { + let controller = new BaseThumbnailController(req, res); + await controller.doPipeline(); +}; //#region WADO-RS Retrieve Transaction Thumbnail Resources @@ -27,7 +35,14 @@ const router = express(); */ router.get( "/studies/:studyUID/thumbnail", - require("./controller/WADO-RS/thumbnail/study") + (req, res, next) => { + req.factory = StudyThumbnailFactory; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get Study's Thumbnail [study UID: ${req.params.studyUID}]`); + next(); + }, + RetrieveThumbnailController ); /** @@ -52,7 +67,14 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/thumbnail", - require("./controller/WADO-RS/thumbnail/series") + (req, res, next) => { + req.factory = SeriesThumbnailFactory; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get Study's Series' Thumbnail [study UID: ${req.params.studyUID}, series UID: ${req.params.seriesUID}]`); + next(); + }, + RetrieveThumbnailController ); /** @@ -78,7 +100,15 @@ router.get( */ router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID/thumbnail", - require("./controller/WADO-RS/thumbnail/instance") + (req, res, next) => { + req.factory = InstanceThumbnailFactory; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get Study's Series' Instance's Thumbnail [study UID: ${req.params.studyUID},`+ + ` series UID: ${req.params.seriesUID}, instance UID: ${req.params.instanceUID}]`); + next(); + }, + RetrieveThumbnailController ); /** @@ -107,8 +137,16 @@ router.get( "/studies/:studyUID/series/:seriesUID/instances/:instanceUID/frames/:frameNumber/thumbnail", validateParams({ frameNumber : intArrayJoi.intArray().items(Joi.number().integer().min(1)).single() - } , "params" , {allowUnknown : true}), - require("./controller/WADO-RS/thumbnail/frame") + } , "params" , {allowUnknown : true}), + (req, res, next) => { + req.factory = InstanceThumbnailFactory; + req.logger = new ApiLogger(req, "WADO-RS"); + req.logger.addTokenValue(); + req.logger.logger.info(`Get Study's Instance's Frame's Thumbnail [study UID: ${req.params.studyUID},`+ + ` series UID: ${req.params.seriesUID}, instance UID: ${req.params.instanceUID}, frame number: ${req.params.frameNumber}]`); + next(); + }, + RetrieveThumbnailController ); diff --git a/api/fhir-convert/controller/dicom-to-fhir.js b/api/fhir-convert/controller/dicom-to-fhir.js new file mode 100644 index 00000000..9bba08db --- /dev/null +++ b/api/fhir-convert/controller/dicom-to-fhir.js @@ -0,0 +1,38 @@ +const { ApiErrorArrayHandler } = require("@error/api-errors.handler"); +const { Controller } = require("@root/api/controller.class"); +const { FhirConvertService } = require("./service/fhir-convert.service"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); + + +class FhirConvertController extends Controller { + constructor(req, res) { + super(req, res); + this.apiLogger = new ApiLogger(req, "fhir-convert"); + this.apiLogger.addTokenValue(); + } + + async mainProcess() { + let fhirConvertService = new FhirConvertService(this.request, this.response); + try { + let fhirJson = await fhirConvertService.convert(); + return this.response + .set("content-type", "application/json") + .status(200) + .json(fhirJson); + } catch (e) { + let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.apiLogger, e); + return apiErrorArrayHandler.doErrorResponse(); + } + } +} + +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let controller = new FhirConvertController(req, res); + + await controller.doPipeline(); +}; diff --git a/api/fhir-convert/controller/service/fhir-convert.service.js b/api/fhir-convert/controller/service/fhir-convert.service.js new file mode 100644 index 00000000..92a6fe7d --- /dev/null +++ b/api/fhir-convert/controller/service/fhir-convert.service.js @@ -0,0 +1,37 @@ +const { DicomJsonToFhir } = require("dicomjson-to-fhir"); +const { raccoonConfig } = require("@root/config-class"); +const Joi = require("joi"); +const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); +const { JDcm2Json } = require("@models/DICOM/dcm4che/dcm2json"); + +const fileSchema = Joi.object({ + files: Joi.object({ + file: Joi.object({ + filepath: Joi.string().required() + }).required() + }).required() +}); + +class FhirConvertService { + constructor(req, res) { + this.request = req; + this.response = res; + } + + async convert() { + let { value, error } = fileSchema.validate(this.request, { allowUnknown : true}); + if (error) { + throw new DicomWebServiceError(DicomWebStatusCodes.InvalidArgumentValue, error.details[0].message, 400); + } + let dicomJson = await JDcm2Json.get(this.request.files.file.filepath); + let protocol = this.request.secure ? "https" : "http"; + let dicomJsonToFhir = new DicomJsonToFhir( + dicomJson, + "raccoon-dicom-web-server", + `${protocol}://${this.request.headers.host}/${raccoonConfig.dicomWebConfig.apiPath}/studies` + ); + return dicomJsonToFhir.getFhirJson(); + } +} + +module.exports.FhirConvertService = FhirConvertService; \ No newline at end of file diff --git a/api/fhir-convert/index.js b/api/fhir-convert/index.js new file mode 100644 index 00000000..03a66f1f --- /dev/null +++ b/api/fhir-convert/index.js @@ -0,0 +1,54 @@ +/** + * Route /fhir-convert + * Implement `DICOM convert to FHIR ImagingStudy, Patient, Endpoint` + * + * @author Chin-Lin Lee + */ + +const Joi = require("joi"); +const { validateByJoi } = require("../validator"); +const express = require("express"); +const router = express.Router(); +const formidable = require("formidable"); + +const formMiddleWare = async (req, res, next) => { + const form = formidable({}); + + form.parse(req, (err, fields, files) => { + if (err) { + next(err); + return; + } + req.fields = fields; + req.files = files; + next(); + }); +}; + +/** + * @openapi + * /fhir-convert: + * post: + * tags: + * - fhir-convert + * description: Convert DICOM to FHIR ImagingStudy, Patient, Endpoint + * requestBody: + * content: + * multipart/form-data: + * schema: + * type: object + * properties: + * file: + * type: string + * format: binary + * encoding: + * file: + * contentType: application/dicom; + * responses: + * "200": + * description: The DICOM instance store successfully + */ +router.post("/", formMiddleWare, require("./controller/dicom-to-fhir")); + + +module.exports = router; \ No newline at end of file diff --git a/app.js b/app.js index 6f195df9..2dd275d3 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,4 @@ -const mongodb = require("./models/mongodb/index"); +require("@dbInitializer"); const express = require("express"); const { createServer } = require("http"); const app = express(); diff --git a/config-class.js b/config-class.js index 1d3d61d3..0d209cbc 100644 --- a/config-class.js +++ b/config-class.js @@ -24,6 +24,7 @@ function generateUidFromGuid(iGuid) { return `2.25.${bigInteger.toString()}`; //Output the previus parsed integer as string by adding `2.25.` as prefix } + class MongoDbConfig { constructor() { this.dbName = env.get("MONGODB_NAME").default("raccoon").asString(); @@ -55,29 +56,23 @@ class DicomWebConfig { } } -class FhirConfig { - constructor() { - this.isSyncToFhir = env.get("SYCN_TO_FHIR_SERVER").default("true").asBool(); - this.baseUrl = env.get("FHIRSERVER_BASE_URL").default("http://127.0.0.1:8089/fhir").asString(); - } -} - class RaccoonConfig { constructor() { - this.mongoDbConfig = new MongoDbConfig(); this.serverConfig = new ServerConfig(); + + this.dbConfig = new MongoDbConfig(); + this.dicomWebConfig = new DicomWebConfig(); this.dicomDimseConfig = new DimseConfig(); - this.fhirConfig = new FhirConfig(); /** @type {string} */ this.mediaStorageUID = generateUidFromGuid( - uuid.v5(this.mongoDbConfig.dbName, NAME_SPACE) + uuid.v5(this.dbConfig.dbName, NAME_SPACE) ); /** @type {string} */ - this.mediaStorageID = this.mongoDbConfig.dbName; + this.mediaStorageID = this.dbConfig.dbName; this.aeTitle = this.dicomDimseConfig.enableDimse ? this.dicomDimseConfig.aeTitle : this.dicomWebConfig.aeTitle; if (!this.aeTitle) diff --git a/config/modula-alias/mongodb/package.json b/config/modula-alias/mongodb/package.json new file mode 100644 index 00000000..db14a9b7 --- /dev/null +++ b/config/modula-alias/mongodb/package.json @@ -0,0 +1,17 @@ +{ + "_moduleAliases": { + "@dcm4che": "../../../models/DICOM/dcm4che/wrapper/org/dcm4che3", + "@java-wrapper": "../../../models/DICOM/dcm4che/wrapper", + "@models": "../../../models", + "@error": "../../../error", + "@root": "../../../", + "@chinlinlee": "../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee", + "@dbModels": "../../../models/mongodb/models", + "@dbInitializer": "../../../models/mongodb/index.js", + "@dicom-json-model": "../../../models/DICOM/dicom-json-model.js", + "@query-dicom-json-factory": "../../../api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory.js", + "@stow-rs-service": "../../../api/dicom-web/controller/STOW-RS/service/stow-rs.service.js", + "@dimse": "../../../dimse", + "@api": "../../../api" + } +} \ No newline at end of file diff --git a/config/modula-alias/sql/package.json b/config/modula-alias/sql/package.json new file mode 100644 index 00000000..01621c48 --- /dev/null +++ b/config/modula-alias/sql/package.json @@ -0,0 +1,16 @@ +{ + "_moduleAliases": { + "@dcm4che": "../../../models/DICOM/dcm4che/wrapper/org/dcm4che3", + "@java-wrapper": "../../../models/DICOM/dcm4che/wrapper", + "@models": "../../../models", + "@error": "../../../error", + "@root": "../../../", + "@chinlinlee": "../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee", + "@dbModels": "../../../models/sql/models", + "@dbInitializer": "../../../models/sql/initializer.js", + "@dicom-json-model": "../../../models/sql/dicom-json-model.js", + "@stow-rs-service": "../../../api/dicom-web/controller/STOW-RS/service/stow-rs.service.js", + "@dimse": "../../../dimse-sql", + "@api": "../../../api-sql" + } +} \ No newline at end of file diff --git a/dimse/c-find.js b/dimse/c-find.js index c519dfaa..824b3a27 100644 --- a/dimse/c-find.js +++ b/dimse/c-find.js @@ -7,11 +7,13 @@ const { PresentationContext } = require("@dcm4che/net/pdu/PresentationContext"); const { QueryRetrieveLevel2 } = require("@dcm4che/net/service/QueryRetrieveLevel2"); const { BasicModCFindSCP } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP"); const { createCFindSCPInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/CFindSCPInject"); -const { JsPatientQueryTask } = require("./patientQueryTask"); -const { JsStudyQueryTask } = require("./studyQueryTask"); -const { JsSeriesQueryTask } = require("./seriesQueryTask"); -const { JsInstanceQueryTask } = require("./instanceQueryTask"); +const { JsPatientQueryTask } = require("@dimse/patientQueryTask"); +const { JsStudyQueryTask } = require("@dimse/studyQueryTask"); +const { JsSeriesQueryTask } = require("@dimse/seriesQueryTask"); +const { JsInstanceQueryTask } = require("@dimse/instanceQueryTask"); const { PATIENT_ROOT_LEVELS, STUDY_ROOT_LEVELS, PATIENT_STUDY_ONLY_LEVELS } = require("./level"); +const { Tag } = require("@dcm4che/data/Tag"); +const { JsMwlQueryTask } = require("./mwlQueryTask"); class JsCFindScp { constructor() { } @@ -59,6 +61,20 @@ class JsCFindScp { return basicModCFindSCP; } + getMwlLevel() { + const cFindScpInject = createCFindSCPInjectProxy(this.getCFindScpInjectProxyMethods(), { + keepAsDaemon: true + }); + + let basicModCFindSCP = new BasicModCFindSCP( + cFindScpInject, + [UID.ModalityWorklistInformationModelFind] + ); + + this.scpObj = basicModCFindSCP; + return basicModCFindSCP; + } + getCFindScpInjectProxyMethods() { /** * @type { import("@java-wrapper/org/github/chinlinlee/dcm777/net/CFindSCPInject").CFindSCPInjectInterface } @@ -83,6 +99,11 @@ class JsCFindScp { }, calculateMatches: async (as, pc, rq, keys) => { try { + let requestSopClassUID = await rq.getString(Tag.AffectedSOPClassUID); + if (requestSopClassUID === UID.ModalityWorklistInformationModelFind ) { + return await (new JsMwlQueryTask(as, pc, rq, keys)).get(); + } + let level = await this.scpObj.getQrLevel(as, pc, rq, keys); if (await level.compareTo(QueryRetrieveLevel2.PATIENT) === 0) { return await (new JsPatientQueryTask(as, pc, rq, keys)).get(); diff --git a/dimse/c-get.js b/dimse/c-get.js index fbeb0f0a..54e54dad 100644 --- a/dimse/c-get.js +++ b/dimse/c-get.js @@ -2,7 +2,7 @@ const { UID } = require("@dcm4che/data/UID"); const { createCGetSCPInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/CGetSCPInject"); const { SimpleCGetSCP } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/SimpleCGetSCP"); const { PATIENT_ROOT_LEVELS, STUDY_ROOT_LEVELS, PATIENT_STUDY_ONLY_LEVELS } = require("./level"); -const { getInstancesFromKeysAttr } = require("./utils"); +const { getInstancesFromKeysAttr } = require("@dimse/utils"); const { RetrieveTaskImpl } = require("@chinlinlee/dcm777/dcmqrscp/RetrieveTaskImpl"); const { createRetrieveAuditInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/dcmqrscp/RetrieveAuditInject"); const { Dimse } = require("@dcm4che/net/Dimse"); diff --git a/dimse/c-move.js b/dimse/c-move.js index 8a8d0bc3..684b552b 100644 --- a/dimse/c-move.js +++ b/dimse/c-move.js @@ -13,7 +13,7 @@ const { AAssociateRQ } = require("@dcm4che/net/pdu/AAssociateRQ"); const { Connection } = require("@dcm4che/net/Connection"); const { RetrieveTaskImpl } = require("@chinlinlee/dcm777/dcmqrscp/RetrieveTaskImpl"); const { Dimse } = require("@dcm4che/net/Dimse"); -const { getInstancesFromKeysAttr } = require("./utils"); +const { getInstancesFromKeysAttr } = require("@dimse/utils"); const { createRetrieveAuditInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/dcmqrscp/RetrieveAuditInject"); const { DimseRetrieveAuditService } = require("./service/retrieveAudit.service"); diff --git a/dimse/c-store.js b/dimse/c-store.js index 30e24b45..4c866a5e 100644 --- a/dimse/c-store.js +++ b/dimse/c-store.js @@ -1,10 +1,8 @@ -const myMongoDB = require("@models/mongodb"); - const path = require("path"); const { createCStoreSCPInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/CStoreSCPInject"); const { default: SimpleCStoreSCP } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/SimpleCStoreSCP"); const { default: File } = require("@java-wrapper/java/io/File"); -const { StowRsService } = require("@root/api/dicom-web/controller/STOW-RS/service/stow-rs.service"); +const { StowRsService } = require("@stow-rs-service"); const { default: Association } = require("@dcm4che/net/Association"); const { PresentationContext } = require("@dcm4che/net/pdu/PresentationContext"); const { Attributes } = require("@dcm4che/data/Attributes"); @@ -33,7 +31,7 @@ const cStoreScpInjectProxy = createCStoreSCPInjectProxy({ socket: { remoteAddress: await association.getCallingAET() } - }, []); + }, { locals: {} }, []); /** @type {formidable.File} */ let fileObj = { diff --git a/dimse/index.js b/dimse/index.js index 5b39ae58..7086d08a 100644 --- a/dimse/index.js +++ b/dimse/index.js @@ -57,6 +57,7 @@ class DcmQrScp { await dicomServiceRegistry.addDicomService(new JsCFindScp().getPatientRootLevel()); await dicomServiceRegistry.addDicomService(new JsCFindScp().getStudyRootLevel()); await dicomServiceRegistry.addDicomService(new JsCFindScp().getPatientStudyOnlyLevel()); + await dicomServiceRegistry.addDicomService(new JsCFindScp().getMwlLevel()); // #endregion // #region C-MOVE @@ -227,7 +228,7 @@ class DcmQrScp { let device = this.connection.getDeviceSync(); try { - if (!raccoonConfig.dicomDimseConfig.keyStore) { + if (raccoonConfig.dicomDimseConfig.keyStore) { device.setKeyManagerSync( SSLManagerFactory.createKeyManagerSync( raccoonConfig.dicomDimseConfig.keyStoreType, diff --git a/dimse/instanceQueryTask.js b/dimse/instanceQueryTask.js index 13cda2bd..39e61cd9 100644 --- a/dimse/instanceQueryTask.js +++ b/dimse/instanceQueryTask.js @@ -3,7 +3,7 @@ const _ = require("lodash"); const { createQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/QueryTaskInject"); const { DimseQueryBuilder } = require("./queryBuilder"); const { JsSeriesQueryTask } = require("./seriesQueryTask"); -const dicomModel = require("@models/mongodb/models/dicom"); +const { InstanceModel } = require("@dbModels/instance.model"); const { InstanceQueryTask } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/InstanceQueryTask"); const { Attributes } = require("@dcm4che/data/Attributes"); const { createInstanceQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/InstanceQueryTaskInject"); @@ -13,6 +13,7 @@ const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); const { UID } = require("@dcm4che/data/UID"); +const { QueryTaskUtils } = require("./utils"); class JsInstanceQueryTask extends JsSeriesQueryTask { @@ -39,108 +40,143 @@ class JsInstanceQueryTask extends JsSeriesQueryTask { ); await super.get(); - await this.instanceQueryTaskInjectMethods.wrappedFindNextInstance(); + await this.instanceQueryTaskInjectProxy.getProxyMethods().wrappedFindNextInstance(); return instanceQueryTask; } getQueryTaskInjectProxy() { - this.instanceBasicQueryTaskInjectMethods = { - hasMoreMatches: () => { - return !_.isNull(this.instanceAttr); - }, - nextMatch: async () => { - let returnAttr = await Attributes.newInstanceAsync( - await this.patientAttr.size() + await this.studyAttr.size() + await this.seriesAttr.size() + await this.instanceAttr.size() - ); - await Attributes.unifyCharacterSets([this.patientAttr, this.studyAttr, this.seriesAttr, this.instanceAttr]); - await returnAttr.addAll(this.patientAttr); - await returnAttr.addAll(this.studyAttr, true); - await returnAttr.addAll(this.seriesAttr, true); - await returnAttr.addAll(this.instanceAttr, true); - - await this.instanceQueryTaskInjectMethods.wrappedFindNextInstance(); - - return returnAttr; - }, - adjust: async (match) => { - return await this.patientAdjust(match); - } - }; - - if (!this.queryTaskInjectProxy) { - this.queryTaskInjectProxy = createQueryTaskInjectProxy(this.instanceBasicQueryTaskInjectMethods); + if (!this.matchIteratorProxy) { + this.matchIteratorProxy = new InstanceMatchIteratorProxy(this); } - - return this.queryTaskInjectProxy; + return this.matchIteratorProxy.get(); } getInstanceQueryTaskInjectProxy() { - /** @type { import("@java-wrapper/org/github/chinlinlee/dcm777/net/InstanceQueryTaskInject").InstanceQueryTaskInjectInterface } */ - this.instanceQueryTaskInjectMethods = { - wrappedFindNextInstance: async () => { - await this.instanceQueryTaskInjectMethods.findNextInstance(); - }, - getInstance: async () => { - this.instance = await this.instanceCursor.next(); - if (this.instance) this.auditDicomInstancesAccessed(); - this.instanceAttr = this.instance ? await this.instance.getAttributes() : null; - }, - findNextInstance: async () => { - if (!this.seriesAttr) - return false; - - if (!this.instanceAttr) { - await this.getNextInstanceCursor(); - await this.instanceQueryTaskInjectMethods.getInstance(); - } else { - await this.instanceQueryTaskInjectMethods.getInstance(); - } - - while (!this.instanceAttr && await this.seriesQueryTaskInjectMethods.findNextSeries()) { - await this.getNextInstanceCursor(); - await this.instanceQueryTaskInjectMethods.getInstance(); - } - - return _.isNull(this.instanceAttr); - } - }; - if (!this.instanceQueryTaskInjectProxy) { - this.instanceQueryTaskInjectProxy = createInstanceQueryTaskInjectProxy(this.instanceQueryTaskInjectMethods); + this.instanceQueryTaskInjectProxy = new InstanceQueryTaskInjectProxy(this); } - return this.instanceQueryTaskInjectProxy; + return this.instanceQueryTaskInjectProxy.get(); } async getNextInstanceCursor() { - let queryAudit = new AuditManager( - EventType.QUERY, EventOutcomeIndicator.Success, - await this.as.getRemoteAET(), await this.as.getRemoteHostName(), - await this.as.getLocalAET(), await this.as.getLocalHostName() - ); + let queryAuditManager = await QueryTaskUtils.getAuditManager(this.as); - let queryAttr = await Attributes.newInstanceAsync(); - await queryAttr.addAll(this.keys); - await queryAttr.addSelected(this.seriesAttr, [Tag.PatientID, Tag.StudyInstanceUID, Tag.SeriesInstanceUID]); - - let queryBuilder = new DimseQueryBuilder(queryAttr, "instance"); - let normalQuery = await queryBuilder.toNormalQuery(); - let mongoQuery = await queryBuilder.getMongoQuery(normalQuery); - queryAudit.onQuery( + let queryAttr = await QueryTaskUtils.getQueryAttribute(this.keys, this.seriesAttr); + let dbQuery = await QueryTaskUtils.getDbQuery(queryAttr, "instance"); + queryAuditManager.onQuery( UID.StudyRootQueryRetrieveInformationModelFind, - JSON.stringify(mongoQuery.$match), + JSON.stringify(dbQuery), "UTF-8" ); - let returnKeys = this.getReturnKeys(normalQuery); + logger.info(`do DIMSE Instance query: ${JSON.stringify(dbQuery)}`); + this.instanceCursor = await InstanceModel.getDimseResultCursor({ + ...dbQuery + }, await QueryTaskUtils.getReturnKeys(queryAttr, "instance")); + } + +} + +class InstanceQueryTaskInjectProxy { + constructor(instanceQueryTask) { + /** @type { JsInstanceQueryTask } */ + this.instanceQueryTask = instanceQueryTask; + } + + get() { + return new createInstanceQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + wrappedFindNextInstance: this.wrappedFindNextInstance.bind(this), + getInstance: this.getInstance.bind(this), + findNextInstance: this.findNextInstance.bind(this) + }; + } + + async wrappedFindNextInstance() { + await this.findNextInstance(); + } + + async findNextInstance() { + if (!this.instanceQueryTask.seriesAttr) + return false; + + if (!this.instanceQueryTask.instanceAttr) { + await this.instanceQueryTask.getNextInstanceCursor(); + await this.getInstance(); + } else { + await this.getInstance(); + } + + while (!this.instanceQueryTask.instanceAttr && await this.instanceQueryTask.seriesQueryTaskInjectProxy.findNextSeries()) { + await this.getNextInstanceCursor(); + await this.getInstance(); + } - logger.info(`do DIMSE Instance query: ${JSON.stringify(mongoQuery.$match)}`); - this.instanceCursor = await dicomModel.getDimseResultCursor({ - ...mongoQuery.$match - }, returnKeys); + return !_.isNull(this.instanceQueryTask.instanceAttr); } + async getInstance() { + this.instanceQueryTask.instance = await this.instanceQueryTask.instanceCursor.next(); + if (this.instanceQueryTask.instance) this.instanceQueryTask.auditDicomInstancesAccessed(); + this.instanceQueryTask.instanceAttr = this.instanceQueryTask.instance ? await this.instanceQueryTask.instance.getAttributes() : null; + } + +} + +class InstanceMatchIteratorProxy { + constructor(instanceQueryTask) { + /** @type {JsInstanceQueryTask} */ + this.instanceQueryTask = instanceQueryTask; + } + + get() { + return createQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + hasMoreMatches: async () => { + return !_.isNull(this.instanceQueryTask.instanceAttr); + }, + nextMatch: async () => { + let returnAttr = await Attributes.newInstanceAsync( + await this.instanceQueryTask.patientAttr.size() + + await this.instanceQueryTask.studyAttr.size() + + await this.instanceQueryTask.seriesAttr.size() + + await this.instanceQueryTask.instanceAttr.size() + ); + + await Attributes.unifyCharacterSets([ + this.instanceQueryTask.patientAttr, + this.instanceQueryTask.studyAttr, + this.instanceQueryTask.seriesAttr, + this.instanceQueryTask.instanceAttr + ]); + await returnAttr.addAll(this.instanceQueryTask.patientAttr); + await returnAttr.addAll(this.instanceQueryTask.studyAttr, true); + await returnAttr.addAll(this.instanceQueryTask.seriesAttr, true); + await returnAttr.addAll(this.instanceQueryTask.instanceAttr, true); + + await this.instanceQueryTask.instanceQueryTaskInjectProxy.wrappedFindNextInstance(); + + return returnAttr; + }, + adjust: async (match) => { + return await this.instanceQueryTask.patientAdjust(match); + } + }; + } } -module.exports.JsInstanceQueryTask = JsInstanceQueryTask; \ No newline at end of file +module.exports.JsInstanceQueryTask = JsInstanceQueryTask; +module.exports.InstanceQueryTaskInjectProxy = InstanceQueryTaskInjectProxy; +module.exports.InstanceMatchIteratorProxy = InstanceMatchIteratorProxy; \ No newline at end of file diff --git a/dimse/mwlQueryTask.js b/dimse/mwlQueryTask.js new file mode 100644 index 00000000..ec63c961 --- /dev/null +++ b/dimse/mwlQueryTask.js @@ -0,0 +1,120 @@ +const _ = require("lodash"); +const { PatientQueryTask } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/PatientQueryTask"); +const { createQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/QueryTaskInject"); +const { Attributes } = require("@dcm4che/data/Attributes"); +const { Tag } = require("@dcm4che/data/Tag"); +const { VR } = require("@dcm4che/data/VR"); +const { Association } = require("@dcm4che/net/Association"); +const { PresentationContext } = require("@dcm4che/net/pdu/PresentationContext"); +const { logger } = require("@root/utils/logs/log"); +const { UID } = require("@dcm4che/data/UID"); +const { QueryTaskUtils } = require("./utils"); +const { default: BasicModQueryTask } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/BasicModQueryTask"); +const { MwlItemModel } = require("@dbModels/mwlitems.model"); + + +class JsMwlQueryTask { + constructor(as, pc, rq, keys) { + /** @type { Association } */ + this.as = as; + /** @type { PresentationContext } */ + this.pc = pc; + /** @type { Attributes } */ + this.rq = rq; + /** @type { Attributes } */ + this.keys = keys; + + this.mwlAttr = null; + this.mwl = null; + } + + async get() { + let mwlQueryTask = await BasicModQueryTask.newInstanceAsync( + this.as, + this.pc, + this.rq, + this.keys, + this.getQueryTaskInjectProxy() + ); + + await this.initCursor(); + + return mwlQueryTask; + } + + getQueryTaskInjectProxy() { + // for creating one + if (!this.matchIteratorProxy) { + this.matchIteratorProxy = new MwlMatchIteratorProxy(this); + } + + return this.matchIteratorProxy.get(); + } + + /** + * + * @param {Attributes} match + * @returns + */ + async basicAdjust(match) { + if (match == null) { + return null; + } + + let filtered = new Attributes(await match.size()); + + await filtered.setNull(Tag.SpecificCharacterSet, VR.CS); + await filtered.addSelected(match, this.keys); + await filtered.supplementEmpty(this.keys); + return filtered; + } + + async initCursor() { + let queryAuditManager = await QueryTaskUtils.getAuditManager(this.as); + let dbQuery = await QueryTaskUtils.getDbQuery(this.keys, "mwl"); + queryAuditManager.onQuery( + UID.ModalityWorklistInformationModelFind, + JSON.stringify(dbQuery), + "UTF-8" + ); + + let returnKeys = await QueryTaskUtils.getReturnKeys(this.keys, "mwl"); + + logger.info(`do DIMSE Modality Work List query: ${JSON.stringify(dbQuery)}`); + this.cursor = await MwlItemModel.getDimseResultCursor({ + ...dbQuery + }, returnKeys); + } +} + +class MwlMatchIteratorProxy { + constructor(mwlQueryTask) { + /** @type {JsMwlQueryTask} */ + this.mwlQueryTask = mwlQueryTask; + } + + get() { + return createQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + hasMoreMatches: async () => { + this.mwlQueryTask.mwl = await this.mwlQueryTask.cursor.next(); + return !_.isNull(this.mwlQueryTask.mwl); + }, + nextMatch: async () => { + this.mwlQueryTask.mwlAttr = this.mwlQueryTask.mwl ? await this.mwlQueryTask.mwl.getAttributes() : null; + return this.mwlQueryTask.mwlAttr; + }, + adjust: async (match) => { + return this.mwlQueryTask.basicAdjust(match); + } + }; + } +} + +module.exports.JsMwlQueryTask = JsMwlQueryTask; +module.exports.MwlMatchIteratorProxy = MwlMatchIteratorProxy; \ No newline at end of file diff --git a/dimse/patientQueryTask.js b/dimse/patientQueryTask.js index 90e7a1d3..f1c74373 100644 --- a/dimse/patientQueryTask.js +++ b/dimse/patientQueryTask.js @@ -6,7 +6,7 @@ const { Attributes } = require("@dcm4che/data/Attributes"); const { Tag } = require("@dcm4che/data/Tag"); const { VR } = require("@dcm4che/data/VR"); const { DimseQueryBuilder } = require("./queryBuilder"); -const patientModel = require("@models/mongodb/models/patient"); +const { PatientModel } = require("@dbModels/patient.model"); const { Association } = require("@dcm4che/net/Association"); const { PresentationContext } = require("@dcm4che/net/pdu/PresentationContext"); const { logger } = require("@root/utils/logs/log"); @@ -14,6 +14,7 @@ const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); const { UID } = require("@dcm4che/data/UID"); +const { QueryTaskUtils } = require("./utils"); class JsPatientQueryTask { @@ -43,56 +44,26 @@ class JsPatientQueryTask { ); await this.initCursor(); - await this.patientQueryTaskInjectMethods.wrappedFindNextPatient(); + await this.patientQueryTaskProxy.wrappedFindNextPatient(); return patientQueryTask; } getQueryTaskInjectProxy() { - /** @type { QueryTaskInjectInterface } */ - this.patientBasicQueryTaskInjectMethods = { - hasMoreMatches: () => { - return !_.isNull(this.patientAttr); - }, - nextMatch: async () => { - let tempRecord = this.patientAttr; - await this.patientQueryTaskInjectMethods.wrappedFindNextPatient(); - return tempRecord; - }, - adjust: async (match) => { - return this.patientAdjust(match); - } - }; - - if (!this.queryTaskInjectProxy) { - this.queryTaskInjectProxy = createQueryTaskInjectProxy(this.patientBasicQueryTaskInjectMethods); + // for creating one + if (!this.matchIteratorProxy) { + this.matchIteratorProxy = new PatientMatchIteratorProxy(this); } - return this.queryTaskInjectProxy; + return this.matchIteratorProxy.get(); } getPatientQueryTaskInjectProxy() { - - /** @type { PatientQueryTaskInjectInterface }*/ - this.patientQueryTaskInjectMethods = { - wrappedFindNextPatient: async () => { - await this.patientQueryTaskInjectMethods.findNextPatient(); - }, - getPatient: async () => { - this.patient = await this.cursor.next(); - this.patientAttr = this.patient ? await this.patient.getAttributes() : null; - }, - findNextPatient: async () => { - await this.patientQueryTaskInjectMethods.getPatient(); - return !_.isNull(this.patientAttr); - } - }; - + // for creating once if (!this.patientQueryTaskProxy) { - this.patientQueryTaskProxy = createPatientQueryTaskInjectProxy(this.patientQueryTaskInjectMethods); + this.patientQueryTaskProxy = new PatientQueryTaskInjectProxy(this); } - - return this.patientQueryTaskProxy; + return this.patientQueryTaskProxy.get(); } /** @@ -134,38 +105,91 @@ class JsPatientQueryTask { return basicAd; } - getReturnKeys(query) { - let returnKeys = {}; - let queryKeys = Object.keys(query); - for (let i = 0; i < queryKeys.length; i++) { - returnKeys[queryKeys[i].split(".").shift()] = 1; - } - return returnKeys; - } - async initCursor() { - let queryAudit = new AuditManager( - EventType.QUERY, - EventOutcomeIndicator.Success, - await this.as.getRemoteAET(), await this.as.getRemoteHostName(), - await this.as.getLocalAET(), await this.as.getLocalHostName() - ); - let queryBuilder = new DimseQueryBuilder(this.keys, "patient"); - let normalQuery = await queryBuilder.toNormalQuery(); - let mongoQuery = await queryBuilder.getMongoQuery(normalQuery); - queryAudit.onQuery( + let queryAuditManager = await QueryTaskUtils.getAuditManager(this.as); + let dbQuery = await QueryTaskUtils.getDbQuery(this.keys, "patient"); + queryAuditManager.onQuery( UID.PatientRootQueryRetrieveInformationModelFind, - JSON.stringify(mongoQuery.$match), + JSON.stringify(dbQuery), "UTF-8" ); - let returnKeys = this.getReturnKeys(normalQuery); + let returnKeys = await QueryTaskUtils.getReturnKeys(this.keys, "patient"); - logger.info(`do DIMSE Patient query: ${JSON.stringify(mongoQuery.$match)}`); - this.cursor = await patientModel.getDimseResultCursor({ - ...mongoQuery.$match + logger.info(`do DIMSE Patient query: ${JSON.stringify(dbQuery)}`); + this.cursor = await PatientModel.getDimseResultCursor({ + ...dbQuery }, returnKeys); } } -module.exports.JsPatientQueryTask = JsPatientQueryTask; \ No newline at end of file +class PatientQueryTaskInjectProxy { + /** + * + * @param {JsPatientQueryTask} queryTask + */ + constructor(patientQueryTask) { + /** @type {JsPatientQueryTask} */ + this.patientQueryTask = patientQueryTask; + } + + get() { + return createPatientQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + wrappedFindNextPatient: this.wrappedFindNextPatient.bind(this), + getPatient: this.getPatient.bind(this), + findNextPatient: this.findNextPatient.bind(this) + }; + } + + async wrappedFindNextPatient() { + await this.findNextPatient(); + } + + async findNextPatient() { + await this.getPatient(); + return !_.isNull(this.patientQueryTask.patientAttr); + } + + async getPatient() { + this.patientQueryTask.patient = await this.patientQueryTask.cursor.next(); + this.patientQueryTask.patientAttr = this.patientQueryTask.patient ? await this.patientQueryTask.patient.getAttributes() : null; + } +} + +class PatientMatchIteratorProxy { + constructor(patientQueryTask) { + /** @type {JsPatientQueryTask} */ + this.patientQueryTask = patientQueryTask; + } + + get() { + return createQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + hasMoreMatches: () => { + return !_.isNull(this.patientQueryTask.patientAttr); + }, + nextMatch: async () => { + let tempRecord = this.patientQueryTask.patientAttr; + await this.patientQueryTask.patientQueryTaskProxy.wrappedFindNextPatient(); + return tempRecord; + }, + adjust: async (match) => { + return this.patientQueryTask.patientAdjust(match); + } + }; + } +} + +module.exports.JsPatientQueryTask = JsPatientQueryTask; +module.exports.PatientMatchIteratorProxy = PatientMatchIteratorProxy; \ No newline at end of file diff --git a/dimse/queryBuilder.js b/dimse/queryBuilder.js index 4c194b85..df81e491 100644 --- a/dimse/queryBuilder.js +++ b/dimse/queryBuilder.js @@ -4,16 +4,17 @@ const { Attributes } = require("@dcm4che/data/Attributes"); const { queryTagsOfEachLevel } = require("./queryTagsOfEachLevel"); const { StringUtils } = require("@dcm4che/util/StringUtils"); const { intTagToString } = require("./utils"); -const { convertRequestQueryToMongoQuery } = require("@root/api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); +const { convertRequestQueryToMongoQuery } = require("@models/mongodb/convertQuery"); +const { default: Tag } = require("@dcm4che/data/Tag"); class DimseQueryBuilder { /** * * @param {Attributes} queryKeys - * @param {"patient" | "study" | "series" | "instance"} level + * @param {"patient" | "study" | "series" | "instance" | "mwl"} level */ - constructor(queryKeys, level="patient") { + constructor(queryKeys, level = "patient") { this.queryKeys = queryKeys; this.level = level; } @@ -21,7 +22,20 @@ class DimseQueryBuilder { async toNormalQuery() { const queryTags = queryTagsOfEachLevel[this.level]; let query = {}; - for (let i = 0 ; i < queryTags.length ; i++) { + + let spsKeys = await this.queryKeys.getNestedDataset(Tag.ScheduledProcedureStepSequence); + if (spsKeys != null && + !(await spsKeys.isEmpty()) && + this.level === "mwl" + ) { + let spsQueryTags = queryTagsOfEachLevel.mwlSps; + for (let i = 0; i < spsQueryTags.length; i++) { + let tagStringValues = await StringUtils.maskNull(await spsKeys.getStrings(spsQueryTags[i])); + query[`${intTagToString(Tag.ScheduledProcedureStepSequence)}.Value.${intTagToString(spsQueryTags[i])}.Value`] = tagStringValues.join(","); + } + } + + for (let i = 0; i < queryTags.length; i++) { let tag = queryTags[i]; /** @type {string[]} */ let tagStringValues = await StringUtils.maskNull(await this.queryKeys.getStrings(tag)); @@ -39,7 +53,7 @@ class DimseQueryBuilder { return clonedQuery; } - async getMongoQuery(query) { + async build(query) { return await convertRequestQueryToMongoQuery( this.cleanEmptyQuery(query) ); diff --git a/dimse/queryTagsOfEachLevel.js b/dimse/queryTagsOfEachLevel.js index aa50060d..aced5b8e 100644 --- a/dimse/queryTagsOfEachLevel.js +++ b/dimse/queryTagsOfEachLevel.js @@ -27,6 +27,53 @@ const queryTagsOfEachLevel = { Tag.SOPInstanceUID, Tag.SOPClassUID, Tag.InstanceNumber + ], + "mwl": [ + Tag.AccessionNumber, + Tag.ReferringPhysicianName, + Tag.ReferencedStudySequence, + Tag.ReferencedPatientSequence, + Tag.PatientName, + Tag.PatientID, + Tag.IssuerOfPatientID, + Tag.IssuerOfPatientIDQualifiersSequence, + Tag.PatientBirthDate, + Tag.PatientSex, + Tag.OtherPatientIDsSequence, + Tag.PatientWeight, + Tag.MedicalAlerts, + Tag.Allergies, + Tag.PregnancyStatus, + Tag.StudyInstanceUID, + Tag.RequestingPhysician, + Tag.RequestedProcedureDescription, + Tag.RequestedProcedureCodeSequence, + Tag.AdmissionID, + Tag.SpecialNeeds, + Tag.CurrentPatientLocation, + Tag.PatientState, + Tag.ScheduledProcedureStepSequence, + Tag.RequestedProcedureID, + Tag.RequestedProcedurePriority, + Tag.PatientTransportArrangements, + Tag.ConfidentialityConstraintOnPatientDataDescription, + Tag.WorklistLabel + ], + "mwlSps": [ + Tag.Modality, + Tag.AnatomicalOrientationType, + Tag.RequestedContrastAgent, + Tag.ScheduledStationAETitle, + Tag.ScheduledProcedureStepStartDate, + Tag.ScheduledProcedureStepStartTime, + Tag.ScheduledPerformingPhysicianName, + Tag.ScheduledProcedureStepDescription, + Tag.ScheduledProtocolCodeSequence, + Tag.ScheduledProcedureStepID, + Tag.ScheduledProcedureStepStatus, + Tag.ScheduledStationName, + Tag.ScheduledProcedureStepLocation, + Tag.PreMedication ] }; diff --git a/dimse/seriesQueryTask.js b/dimse/seriesQueryTask.js index 9cdc11d1..7d20e19e 100644 --- a/dimse/seriesQueryTask.js +++ b/dimse/seriesQueryTask.js @@ -3,7 +3,7 @@ const _ = require("lodash"); const { createQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/QueryTaskInject"); const { DimseQueryBuilder } = require("./queryBuilder"); const { JsStudyQueryTask } = require("./studyQueryTask"); -const dicomSeriesModel = require("@models/mongodb/models/dicomSeries"); +const { SeriesModel } = require("@dbModels/series.model"); const { SeriesQueryTask } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/SeriesQueryTask"); const { Attributes } = require("@dcm4che/data/Attributes"); const { createSeriesQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/SeriesQueryTaskInject"); @@ -13,6 +13,7 @@ const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); const { UID } = require("@dcm4che/data/UID"); +const { QueryTaskUtils } = require("./utils"); class JsSeriesQueryTask extends JsStudyQueryTask { constructor(as, pc, rq, keys) { @@ -37,107 +38,139 @@ class JsSeriesQueryTask extends JsStudyQueryTask { ); await super.get(); - await this.seriesQueryTaskInjectMethods.wrappedFindNextSeries(); + await this.seriesQueryTaskInjectProxy.wrappedFindNextSeries(); return seriesQueryTask; } getQueryTaskInjectProxy() { - /** @type { QueryTaskInjectInterface } */ - this.seriesBasicQueryTaskInjectMethods = { - hasMoreMatches: () => { - return !_.isNull(this.seriesAttr); - }, - nextMatch: async () => { - let returnAttr = await Attributes.newInstanceAsync( - await this.patientAttr.size() + await this.studyAttr.size() + await this.seriesAttr.size() - ); - await Attributes.unifyCharacterSets([this.patientAttr, this.studyAttr, this.seriesAttr]); - await returnAttr.addAll(this.patientAttr); - await returnAttr.addAll(this.studyAttr, true); - await returnAttr.addAll(this.seriesAttr, true); - - await this.seriesQueryTaskInjectMethods.wrappedFindNextSeries(); - - return returnAttr; - }, - adjust: async (match) => { - return await this.patientAdjust(match); - } - }; - - if (!this.queryTaskInjectProxy) { - this.queryTaskInjectProxy = createQueryTaskInjectProxy(this.seriesBasicQueryTaskInjectMethods); + if (!this.matchIteratorProxy) { + this.matchIteratorProxy = new SeriesMatchIteratorProxy(this); } - return this.queryTaskInjectProxy; + return this.matchIteratorProxy.get(); } getSeriesQueryTaskInjectProxy() { - /** @type {import("@java-wrapper/org/github/chinlinlee/dcm777/net/SeriesQueryTaskInject").SeriesQueryTaskInjectInterface} */ - this.seriesQueryTaskInjectMethods = { - wrappedFindNextSeries: async () => { - await this.seriesQueryTaskInjectMethods.findNextSeries(); - }, - getSeries: async () => { - this.series = await this.seriesCursor.next(); - if (this.series) this.auditDicomInstancesAccessed(); - this.seriesAttr = this.series ? await this.series.getAttributes() : null; - }, - findNextSeries: async () => { - if (!this.studyAttr) - return false; - - if (!this.seriesAttr) { - await this.getNextSeriesCursor(); - await this.seriesQueryTaskInjectMethods.getSeries(); - } else { - await this.seriesQueryTaskInjectMethods.getSeries(); - } - - while (!this.seriesAttr && await this.studyQueryTaskInjectMethods.findNextStudy()) { - await this.getNextSeriesCursor(); - await this.seriesQueryTaskInjectMethods.getSeries(); - } - - return !_.isNull(this.seriesAttr); - } - }; - if (!this.seriesQueryTaskInjectProxy) { - this.seriesQueryTaskInjectProxy = createSeriesQueryTaskInjectProxy(this.seriesQueryTaskInjectMethods); + this.seriesQueryTaskInjectProxy = new SeriesQueryTaskInjectProxy(this); } - return this.seriesQueryTaskInjectProxy; + return this.seriesQueryTaskInjectProxy.get(); } async getNextSeriesCursor() { - let queryAudit = new AuditManager( - EventType.QUERY, EventOutcomeIndicator.Success, - await this.as.getRemoteAET(), await this.as.getRemoteHostName(), - await this.as.getLocalAET(), await this.as.getLocalHostName() - ); + let queryAuditManager = await QueryTaskUtils.getAuditManager(this.as); - let queryAttr = await Attributes.newInstanceAsync(); - await queryAttr.addAll(this.keys); - await queryAttr.addSelected(this.studyAttr, [Tag.PatientID, Tag.StudyInstanceUID]); - - let queryBuilder = new DimseQueryBuilder(queryAttr, "series"); - let normalQuery = await queryBuilder.toNormalQuery(); - let mongoQuery = await queryBuilder.getMongoQuery(normalQuery); - queryAudit.onQuery( + let queryAttr = await QueryTaskUtils.getQueryAttribute(this.keys, this.studyAttr); + let dbQuery = await QueryTaskUtils.getDbQuery(queryAttr, "series"); + queryAuditManager.onQuery( UID.StudyRootQueryRetrieveInformationModelFind, - JSON.stringify(mongoQuery.$match), + JSON.stringify(dbQuery), "UTF-8" ); - let returnKeys = this.getReturnKeys(normalQuery); + logger.info(`do DIMSE Series query: ${JSON.stringify(dbQuery)}`); + this.seriesCursor = await SeriesModel.getDimseResultCursor({ + ...dbQuery + }, await QueryTaskUtils.getReturnKeys(queryAttr, "series")); + } +} +class SeriesQueryTaskInjectProxy { + constructor(seriesQueryTask) { + /** @type { JsSeriesQueryTask } */ + this.seriesQueryTask = seriesQueryTask; + } + + get() { + return new createSeriesQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + wrappedFindNextSeries: this.wrappedFindNextSeries.bind(this), + getSeries: this.getSeries.bind(this), + findNextSeries: this.findNextSeries.bind(this) + }; + } - logger.info(`do DIMSE Series query: ${JSON.stringify(mongoQuery.$match)}`); - this.seriesCursor = await dicomSeriesModel.getDimseResultCursor({ - ...mongoQuery.$match - }, returnKeys); + async wrappedFindNextSeries() { + await this.findNextSeries(); + } + + async getSeries() { + this.seriesQueryTask.series = await this.seriesQueryTask.seriesCursor.next(); + if (this.seriesQueryTask.series) this.seriesQueryTask.auditDicomInstancesAccessed(); + this.seriesQueryTask.seriesAttr = this.seriesQueryTask.series ? await this.seriesQueryTask.series.getAttributes() : null; + } + + async findNextSeries() { + if (!this.seriesQueryTask.studyAttr) + return false; + + if (!this.seriesQueryTask.seriesAttr) { + await this.seriesQueryTask.getNextSeriesCursor(); + await this.getSeries(); + } else { + await this.getSeries(); + } + + while (!this.seriesQueryTask.seriesAttr && await this.seriesQueryTask.studyQueryTaskInjectProxy.findNextStudy()) { + await this.seriesQueryTask.getNextSeriesCursor(); + await this.getSeries(); + } + + return !_.isNull(this.seriesQueryTask.seriesAttr); + } +} + +class SeriesMatchIteratorProxy { + constructor(seriesQueryTask) { + /** @type {JsSeriesQueryTask} */ + this.seriesQueryTask = seriesQueryTask; + } + + get() { + return createQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + hasMoreMatches: () => { + return !_.isNull(this.seriesQueryTask.seriesAttr); + }, + nextMatch: async () => { + let returnAttr = await Attributes.newInstanceAsync( + await this.seriesQueryTask.patientAttr.size() + + await this.seriesQueryTask.studyAttr.size() + + await this.seriesQueryTask.seriesAttr.size() + ); + + await Attributes.unifyCharacterSets([ + this.seriesQueryTask.patientAttr, + this.seriesQueryTask.studyAttr, + this.seriesQueryTask.seriesAttr + ]); + + await returnAttr.addAll(this.seriesQueryTask.patientAttr); + await returnAttr.addAll(this.seriesQueryTask.studyAttr, true); + await returnAttr.addAll(this.seriesQueryTask.seriesAttr, true); + + await this.seriesQueryTask.seriesQueryTaskInjectProxy.wrappedFindNextSeries(); + + return returnAttr; + }, + adjust: async (match) => { + return this.seriesQueryTask.patientAdjust(match); + } + }; } } -module.exports.JsSeriesQueryTask = JsSeriesQueryTask; \ No newline at end of file +module.exports.JsSeriesQueryTask = JsSeriesQueryTask; +module.exports.SeriesQueryTaskInjectProxy = SeriesQueryTaskInjectProxy; +module.exports.SeriesMatchIteratorProxy = SeriesMatchIteratorProxy; \ No newline at end of file diff --git a/dimse/studyQueryTask.js b/dimse/studyQueryTask.js index 87ea9c9a..c94960ab 100644 --- a/dimse/studyQueryTask.js +++ b/dimse/studyQueryTask.js @@ -4,15 +4,16 @@ const { StudyQueryTask } = require("@chinlinlee/dcm777/net/StudyQueryTask"); const { JsPatientQueryTask } = require("./patientQueryTask"); const { Tag } = require("@dcm4che/data/Tag"); const { createQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/QueryTaskInject"); -const { StudyQueryTaskInjectInterface, createStudyQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/StudyQueryTaskInject"); +const { createStudyQueryTaskInjectProxy } = require("@java-wrapper/org/github/chinlinlee/dcm777/net/StudyQueryTaskInject"); const { DimseQueryBuilder } = require("./queryBuilder"); -const dicomStudyModel = require("@models/mongodb/models/dicomStudy"); +const { StudyModel } = require("@dbModels/study.model"); const { Attributes } = require("@dcm4che/data/Attributes"); const { logger } = require("@root/utils/logs/log"); const { AuditManager } = require("@models/DICOM/audit/auditManager"); const { EventType } = require("@models/DICOM/audit/eventType"); const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); const { UID } = require("@dcm4che/data/UID"); +const { QueryTaskUtils } = require("./utils"); class JsStudyQueryTask extends JsPatientQueryTask { constructor(as, pc, rq, keys) { @@ -36,105 +37,42 @@ class JsStudyQueryTask extends JsPatientQueryTask { ); await super.get(); - await this.studyQueryTaskInjectMethods.wrappedFindNextStudy(); + await this.studyQueryTaskInjectProxy.wrappedFindNextStudy(); return studyQueryTask; } getQueryTaskInjectProxy() { - /** @type { QueryTaskInjectInterface } */ - this.studyBasicQueryTaskInjectMethods = { - hasMoreMatches: () => { - return !_.isNull(this.studyAttr); - }, - nextMatch: async () => { - let returnAttr = await Attributes.newInstanceAsync( - await this.patientAttr.size() + await this.studyAttr.size() - ); - await Attributes.unifyCharacterSets([this.patientAttr, this.studyAttr]); - await returnAttr.addAll(this.patientAttr); - await returnAttr.addAll(this.studyAttr); - - await this.studyQueryTaskInjectMethods.wrappedFindNextStudy(); - - return returnAttr; - }, - adjust: async (match) => { - return await this.patientAdjust(match); - } - }; - if (!this.queryTaskInjectProxy) { - this.queryTaskInjectProxy = createQueryTaskInjectProxy(this.studyBasicQueryTaskInjectMethods); + this.queryTaskInjectProxy = new StudyMatchIteratorProxy(this); } - return this.queryTaskInjectProxy; + return this.queryTaskInjectProxy.get(); } getStudyQueryTaskInjectProxy() { - /** @type { StudyQueryTaskInjectInterface } */ - this.studyQueryTaskInjectMethods = { - wrappedFindNextStudy: async () => { - await this.studyQueryTaskInjectMethods.findNextStudy(); - }, - getStudy: async () => { - this.study = await this.studyCursor.next(); - this.auditDicomInstancesAccessed(); - this.studyAttr = this.study ? await this.study.getAttributes() : null; - }, - findNextStudy: async () => { - if (!this.patientAttr) - return false; - - if (!this.studyAttr) { - await this.getNextStudyCursor(); - await this.studyQueryTaskInjectMethods.getStudy(); - } else { - await this.studyQueryTaskInjectMethods.getStudy(); - } - - while (!this.studyAttr && await this.patientQueryTaskInjectMethods.findNextPatient()) { - await this.getNextStudyCursor(); - await this.studyQueryTaskInjectMethods.getStudy(); - } - - return !_.isNull(this.studyAttr); - } - }; - if (!this.studyQueryTaskInjectProxy) { - this.studyQueryTaskInjectProxy = createStudyQueryTaskInjectProxy(this.studyQueryTaskInjectMethods); + this.studyQueryTaskInjectProxy = new StudyQueryTaskInjectProxy(this); } - return this.studyQueryTaskInjectProxy; + return this.studyQueryTaskInjectProxy.get(); } async getNextStudyCursor() { - let queryAudit = new AuditManager( - EventType.QUERY, EventOutcomeIndicator.Success, - await this.as.getRemoteAET(), await this.as.getRemoteHostName(), - await this.as.getLocalAET(), await this.as.getLocalHostName() - ); - - let queryAttr = await Attributes.newInstanceAsync(); - await queryAttr.addAll(this.keys); - await queryAttr.addSelected(this.patientAttr, [Tag.PatientID]); - - let queryBuilder = new DimseQueryBuilder(queryAttr, "study"); - let normalQuery = await queryBuilder.toNormalQuery(); - let mongoQuery = await queryBuilder.getMongoQuery(normalQuery); - queryAudit.onQuery( + let queryAuditManager = await QueryTaskUtils.getAuditManager(this.as); + + let queryAttr = await QueryTaskUtils.getQueryAttribute(this.keys, this.patientAttr); + let dbQuery = await QueryTaskUtils.getDbQuery(queryAttr, "study"); + queryAuditManager.onQuery( UID.StudyRootQueryRetrieveInformationModelFind, - JSON.stringify(mongoQuery.$match), + JSON.stringify(dbQuery), "UTF-8" ); - let returnKeys = this.getReturnKeys(normalQuery); - - logger.info(`do DIMSE Study query: ${JSON.stringify(mongoQuery.$match)}`); - this.studyCursor = await dicomStudyModel.getDimseResultCursor({ - ...mongoQuery.$match - }, returnKeys); + logger.info(`do DIMSE Study query: ${JSON.stringify(dbQuery)}`); + this.studyCursor = await StudyModel.getDimseResultCursor({ + ...dbQuery + }, await QueryTaskUtils.getReturnKeys(queryAttr, "study")); } async auditDicomInstancesAccessed() { @@ -146,10 +84,100 @@ class JsStudyQueryTask extends JsPatientQueryTask { await this.as.getRemoteAET(), await this.as.getRemoteHostName(), await this.as.getLocalAET(), await this.as.getLocalHostName() ); - let studyUID = _.get(this.study, "0020000D.Value.0"); auditManager.onDicomInstancesAccessed([studyUID]); } } -module.exports.JsStudyQueryTask = JsStudyQueryTask; \ No newline at end of file +class StudyQueryTaskInjectProxy { + constructor(studyQueryTask) { + /** @type { JsStudyQueryTask } */ + this.studyQueryTask = studyQueryTask; + } + + get() { + return createStudyQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + wrappedFindNextStudy: this.wrappedFindNextStudy.bind(this), + getStudy: this.getStudy.bind(this), + findNextStudy: this.findNextStudy.bind(this) + }; + } + + async wrappedFindNextStudy() { + await this.findNextStudy(); + } + + async getStudy() { + this.studyQueryTask.study = await this.studyQueryTask.studyCursor.next(); + this.studyQueryTask.auditDicomInstancesAccessed(); + this.studyQueryTask.studyAttr = this.studyQueryTask.study ? await this.studyQueryTask.study.getAttributes() : null; + } + + async findNextStudy() { + if (!this.studyQueryTask.patientAttr) + return false; + + if (!this.studyQueryTask.studyAttr) { + await this.studyQueryTask.getNextStudyCursor(); + await this.getStudy(); + } else { + await this.getStudy(); + } + + while (!this.studyQueryTask.studyAttr && await this.studyQueryTask.patientQueryTaskProxy.findNextPatient()) { + await this.studyQueryTask.getNextStudyCursor(); + await this.getStudy(); + } + + return !_.isNull(this.studyQueryTask.studyAttr); + } +} + +class StudyMatchIteratorProxy { + constructor(studyQueryTask) { + /** @type { JsStudyQueryTask } */ + this.studyQueryTask = studyQueryTask; + } + + get() { + return createQueryTaskInjectProxy(this.getProxyMethods(), { + keepAsDaemon: true + }); + } + + getProxyMethods() { + return { + hasMoreMatches: () => { + return !_.isNull(this.studyQueryTask.studyAttr); + }, + nextMatch: async () => { + let returnAttr = await Attributes.newInstanceAsync( + await this.studyQueryTask.patientAttr.size() + await this.studyQueryTask.studyAttr.size() + ); + await Attributes.unifyCharacterSets([ + this.studyQueryTask.patientAttr, + this.studyQueryTask.studyAttr + ]); + await returnAttr.addAll(this.studyQueryTask.patientAttr); + await returnAttr.addAll(this.studyQueryTask.studyAttr); + + await this.studyQueryTask.studyQueryTaskInjectProxy.wrappedFindNextStudy(); + + return returnAttr; + }, + adjust: async (match) => { + return this.studyQueryTask.patientAdjust(match); + } + }; + } +} + +module.exports.JsStudyQueryTask = JsStudyQueryTask; +module.exports.StudyMatchIteratorProxy = StudyMatchIteratorProxy; +module.exports.StudyQueryTaskInjectProxy = StudyQueryTaskInjectProxy; \ No newline at end of file diff --git a/dimse/utils.js b/dimse/utils.js index 048c5d95..67cef4a4 100644 --- a/dimse/utils.js +++ b/dimse/utils.js @@ -6,6 +6,10 @@ const { importClass } = require("java-bridge"); const { raccoonConfig } = require("@root/config-class"); const { InstanceLocator } = require("@dcm4che/net/service/InstanceLocator"); const { File } = require("@java-wrapper/java/io/File"); +const { AuditManager } = require("@models/DICOM/audit/auditManager"); +const { EventType } = require("@models/DICOM/audit/eventType"); +const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils"); +const { Tag } = require("@dcm4che/data/Tag"); /** * * @param {number} tag @@ -14,29 +18,26 @@ function intTagToString(tag) { return tag.toString(16).padStart(8, "0").toUpperCase(); } +const INSTANCE_RETURN_KEYS = { + "instancePath": 1, + "00020010": 1, + "00080016": 1, + "00080018": 1, + "0020000D": 1, + "0020000E": 1 +}; + /** * * @param {Attributes} keys * @returns */ async function getInstancesFromKeysAttr(keys) { - const { DimseQueryBuilder } = require("./queryBuilder"); - let queryBuilder = new DimseQueryBuilder(keys, "instance"); - let normalQuery = await queryBuilder.toNormalQuery(); - let mongoQuery = await queryBuilder.getMongoQuery(normalQuery); - - let returnKeys = { - "instancePath": 1, - "00020010": 1, - "00080016": 1, - "00080018": 1, - "0020000D": 1, - "0020000E": 1 - }; + let dbQuery = await QueryTaskUtils.getDbQuery(keys, "instance"); let instances = await mongoose.model("dicom").find({ - ...mongoQuery.$match - }, returnKeys).setOptions({ + ...dbQuery + }, INSTANCE_RETURN_KEYS).setOptions({ strictQuery: false }).exec(); const JArrayList = await importClass("java.util.ArrayList"); @@ -44,9 +45,11 @@ async function getInstancesFromKeysAttr(keys) { for (let instance of instances) { let instanceFile = await File.newInstanceAsync( - path.join( - raccoonConfig.dicomWebConfig.storeRootPath, - instance.instancePath + path.resolve( + path.join( + raccoonConfig.dicomWebConfig.storeRootPath, + instance.instancePath + ) ) ); @@ -72,29 +75,71 @@ async function getInstancesFromKeysAttr(keys) { * @returns */ async function findOneInstanceFromKeysAttr(keys) { - const { DimseQueryBuilder } = require("./queryBuilder"); - let queryBuilder = new DimseQueryBuilder(keys, "instance"); - let normalQuery = await queryBuilder.toNormalQuery(); - let mongoQuery = await queryBuilder.getMongoQuery(normalQuery); - - let returnKeys = { - "instancePath": 1, - "00020010": 1, - "00080016": 1, - "00080018": 1, - "0020000D": 1, - "0020000E": 1 - }; + let dbQuery = await QueryTaskUtils.getDbQuery(keys, "instance"); let instance = await mongoose.model("dicom").findOne({ - ...mongoQuery.$match - }, returnKeys).setOptions({ + ...dbQuery + }, INSTANCE_RETURN_KEYS).setOptions({ strictQuery: false }).exec(); return instance; } +const QUERY_ATTR_SELECTED_TAGS = { + "patient": [Tag.PatientID], + "study": [Tag.PatientID], + "series": [Tag.PatientID, Tag.StudyInstanceUID], + "instance": [Tag.PatientID, Tag.StudyInstanceUID, Tag.SeriesInstanceUID] +}; +class QueryTaskUtils { + /** + * + * @param {import("@dcm4che/net/Association").Association} association + * @returns + */ + static async getAuditManager(association) { + return new AuditManager( + EventType.QUERY, EventOutcomeIndicator.Success, + await association.getRemoteAET(), await association.getRemoteHostName(), + await association.getLocalAET(), await association.getLocalHostName() + ); + } + + static async getQueryAttribute(keys, parentAttr, level = "patient") { + let queryAttr = await Attributes.newInstanceAsync(); + await Attributes.unifyCharacterSets([keys, parentAttr]); + await queryAttr.addAll(keys); + await queryAttr.addSelected(parentAttr, QUERY_ATTR_SELECTED_TAGS[level]); + return queryAttr; + } + + static async getQueryBuilder(queryAttr, level = "patient") { + const { DimseQueryBuilder } = require("@dimse/queryBuilder"); + return new DimseQueryBuilder(queryAttr, level); + } + + static async getReturnKeys(queryAttr, level = "patient") { + let queryBuilder = await QueryTaskUtils.getQueryBuilder(queryAttr, level); + let query = await queryBuilder.toNormalQuery(); + let returnKeys = {}; + let queryKeys = Object.keys(query); + for (let i = 0; i < queryKeys.length; i++) { + returnKeys[queryKeys[i].split(".").shift()] = 1; + } + return returnKeys; + } + + static async getDbQuery(queryAttr, level="patient") { + let queryBuilder = await QueryTaskUtils.getQueryBuilder(queryAttr, level); + let normalQuery = await queryBuilder.toNormalQuery(); + let dbQuery = await queryBuilder.build(normalQuery); + + return dbQuery.$match; + } +} + module.exports.intTagToString = intTagToString; module.exports.getInstancesFromKeysAttr = getInstancesFromKeysAttr; -module.exports.findOneInstanceFromKeysAttr = findOneInstanceFromKeysAttr; \ No newline at end of file +module.exports.findOneInstanceFromKeysAttr = findOneInstanceFromKeysAttr; +module.exports.QueryTaskUtils = QueryTaskUtils; \ No newline at end of file diff --git a/get-swagger.js b/docs/get-swagger.js similarity index 68% rename from get-swagger.js rename to docs/get-swagger.js index 03478058..f6050900 100644 --- a/get-swagger.js +++ b/docs/get-swagger.js @@ -1,3 +1,4 @@ +const path = require("path"); const swaggerJsDoc = require("swagger-jsdoc"); const fsP = require("fs").promises; @@ -19,14 +20,17 @@ const fsP = require("fs").promises; // Path to the API docs // Note that this path is relative to the current directory from which the Node.js is ran, not the application itself. apis: [ - `${__dirname}/api/**/*.js`, - `${__dirname}/docs/swagger/parameters/*.yaml`, - `${__dirname}/docs/swagger/schemas/*.yaml`, - `${__dirname}/docs/swagger/responses/*.yaml` + `${__dirname}/../api/**/*.js`, + `${__dirname}/../docs/swagger/parameters/*.yaml`, + `${__dirname}/../docs/swagger/schemas/*.yaml`, + `${__dirname}/../docs/swagger/responses/*.yaml` ] }; const swaggerSpec = await swaggerJsDoc(options); console.log(JSON.stringify(swaggerSpec, null, 4)); - await fsP.writeFile("docs/swagger/openapi.json", JSON.stringify(swaggerSpec, null, 4)); + await fsP.writeFile( + path.join(__dirname, "./swagger/openapi.json"), + JSON.stringify(swaggerSpec, null, 4) + ); })(); diff --git a/docs/swagger/openapi.json b/docs/swagger/openapi.json index 65a0bc3f..a888a248 100644 --- a/docs/swagger/openapi.json +++ b/docs/swagger/openapi.json @@ -6,6 +6,244 @@ }, "openapi": "3.0.0", "paths": { + "/dicom-web/mwlitems": { + "post": { + "tags": [ + "MWL-RS" + ], + "description": "This transaction create or update a Modality WorkList item.\n", + "responses": { + "200": { + "description": "The workitem create successfully" + } + } + }, + "get": { + "tags": [ + "MWL-RS" + ], + "description": "This transaction search Modality WorkList items.\n", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + } + ], + "responses": { + "200": { + "description": "Query successfully", + "content": { + "application/dicom+json": { + "schema": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + } + }, + "/dicom-web/mwlitems/count": { + "get": { + "tags": [ + "MWL-RS" + ], + "description": "This transaction get Modality WorkList items count.\n", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + } + ], + "responses": { + "200": { + "description": "Query successfully", + "content": { + "application/dicom+json": { + "schema": { + "properties": { + "count": { + "type": "number" + } + } + } + } + } + } + } + } + }, + "/dicom-web/mwlitems/{studyUID}/{spsID}": { + "delete": { + "tags": [ + "MWL-RS" + ], + "description": "This transaction deletes a Modality WorkList item.\n", + "parameters": [ + { + "$ref": "#/components/parameters/studyUID" + }, + { + "$ref": "#/components/parameters/spsID" + } + ], + "responses": { + "204": { + "description": "Delete successfully" + } + } + } + }, + "/dicom-web/mwlitems/{studyUID}/{spsID}/status/{spsStatus}": { + "post": { + "tags": [ + "MWL-RS" + ], + "description": "This transaction create or update a Modality WorkList item.\n", + "parameters": [ + { + "$ref": "#/components/parameters/studyUID" + }, + { + "$ref": "#/components/parameters/spsID" + }, + { + "$ref": "#/components/parameters/spsStatus" + } + ], + "responses": { + "200": { + "description": "change status of mwl item successfully" + } + } + } + }, + "/dicom-web/mwlitems/status/{spsStatus}": { + "post": { + "tags": [ + "MWL-RS" + ], + "description": "This transaction create or update a Modality WorkList item.\n", + "parameters": [ + { + "$ref": "#/components/parameters/spsStatus" + }, + { + "$ref": "#/components/parameters/filter" + } + ], + "responses": { + "200": { + "description": "change status of mwl items successfully" + } + } + } + }, + "/dicom-web/patients": { + "post": { + "tags": [ + "PAM-RS" + ], + "description": "Create new patient", + "responses": { + "200": { + "description": "Create patient successfully", + "content": { + "application/dicom+json": { + "schema": { + "type": "object", + "properties": { + "patientID": { + "type": "string" + } + } + } + } + } + } + } + }, + "get": { + "tags": [ + "QIDO-RS" + ], + "description": "Query all patients in server", + "parameters": [ + { + "$ref": "#/components/parameters/PatientName" + }, + { + "$ref": "#/components/parameters/PatientID" + }, + { + "$ref": "#/components/parameters/PatientBirthDate" + }, + { + "$ref": "#/components/parameters/PatientBirthTime" + } + ], + "responses": { + "200": { + "description": "Query successfully", + "content": { + "application/dicom+json": { + "schema": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/PatientRequiredMatchingAttributes" + } + ] + } + } + } + } + } + } + } + }, + "/dicom-web/patients/{patientID}": { + "put": { + "tags": [ + "PAM-RS" + ], + "description": "Create new patient", + "parameters": [ + { + "$ref": "#/components/parameters/patientID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/dicom+json": { + "schema": { + "$ref": "#/components/schemas/PatientRequiredMatchingAttributes" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatientRequiredMatchingAttributes" + } + } + } + }, + "responses": { + "200": { + "description": "Create patient successfully", + "content": { + "application/dicom+json": { + "schema": { + "$ref": "#/components/schemas/PatientRequiredMatchingAttributes" + } + } + } + } + } + } + }, "/dicom-web/studies": { "get": { "tags": [ @@ -36,6 +274,9 @@ }, { "$ref": "#/components/parameters/StudyID" + }, + { + "$ref": "#/components/parameters/isRecycle" } ], "responses": { @@ -129,6 +370,9 @@ }, { "$ref": "#/components/parameters/SeriesNumber" + }, + { + "$ref": "#/components/parameters/isRecycle" } ], "responses": { @@ -200,6 +444,9 @@ }, { "$ref": "#/components/parameters/InstanceNumber" + }, + { + "$ref": "#/components/parameters/isRecycle" } ], "responses": { @@ -277,6 +524,9 @@ }, { "$ref": "#/components/parameters/InstanceNumber" + }, + { + "$ref": "#/components/parameters/isRecycle" } ], "responses": { @@ -342,6 +592,9 @@ }, { "$ref": "#/components/parameters/SeriesNumber" + }, + { + "$ref": "#/components/parameters/isRecycle" } ], "responses": { @@ -410,6 +663,9 @@ }, { "$ref": "#/components/parameters/InstanceNumber" + }, + { + "$ref": "#/components/parameters/isRecycle" } ], "responses": { @@ -439,47 +695,6 @@ } } }, - "/dicom-web/patients": { - "get": { - "tags": [ - "QIDO-RS" - ], - "description": "Query all patients in server", - "parameters": [ - { - "$ref": "#/components/parameters/PatientName" - }, - { - "$ref": "#/components/parameters/PatientID" - }, - { - "$ref": "#/components/parameters/PatientBirthDate" - }, - { - "$ref": "#/components/parameters/PatientBirthTime" - } - ], - "responses": { - "200": { - "description": "Query successfully", - "content": { - "application/dicom+json": { - "schema": { - "type": "array", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/PatientRequiredMatchingAttributes" - } - ] - } - } - } - } - } - } - } - }, "/dicom-web/workitems": { "post": { "tags": [ @@ -508,7 +723,10 @@ "content": { "application/dicom+json": { "schema": { - "type": "array" + "type": "array", + "items": { + "type": "object" + } } } } @@ -533,7 +751,10 @@ "content": { "application/dicom+json": { "schema": { - "type": "array" + "type": "array", + "items": { + "type": "object" + } } } } @@ -1100,6 +1321,39 @@ } } }, + "/fhir-convert": { + "post": { + "tags": [ + "fhir-convert" + ], + "description": "Convert DICOM to FHIR ImagingStudy, Patient, Endpoint", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + } + } + }, + "encoding": { + "file": { + "contentType": "application/dicom;" + } + } + } + } + }, + "responses": { + "200": { + "description": "The DICOM instance store successfully" + } + } + } + }, "/wado": { "get": { "tags": [ @@ -1243,6 +1497,51 @@ } }, "parameters": { + "filter": { + "name": "filter", + "description": "{attributeID}={value}; {attributeID} = {dicomTag} | {dicomKeyword} | {dicomTag}.{attributeID} | {dicomKeyword}.{attributeID}", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "isRecycle": { + "name": "isRecycle", + "description": "Query if the instance is in recycle bin, default is false, and notice that this one is not standard by DICOM standard (i.e. raccoon custom parameter)", + "in": "query", + "schema": { + "type": "boolean" + } + }, + "spsID": { + "in": "path", + "name": "spsID", + "required": true, + "schema": { + "type": "string" + } + }, + "spsStatus": { + "in": "path", + "name": "spsStatus", + "required": true, + "schema": { + "type": "string", + "enum": [ + "SCHEDULED", + "ARRIVED", + "READY", + "STARTED", + "DEPARTED", + "CANCELED", + "DISCONTINUED", + "COMPLETED" + ] + } + }, "PatientName": { "name": "00100010", "description": "Patient's full name.", @@ -1275,6 +1574,15 @@ "type": "string" } }, + "patientID": { + "name": "patientID", + "description": "Patient ID", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, "studyUID": { "name": "studyUID", "in": "path", @@ -1431,7 +1739,8 @@ "binaryValuePath": { "name": "binaryValuePath", "description": "{dicomTag} or {dicomTag.InlineBinary} or {dicomTag.Value.itemIndex.Tag}", - "in": "query", + "in": "path", + "required": true, "schema": { "type": "string" } diff --git a/docs/swagger/parameters/dicomweb-common.yaml b/docs/swagger/parameters/dicomweb-common.yaml index b2c1448b..f8137957 100644 --- a/docs/swagger/parameters/dicomweb-common.yaml +++ b/docs/swagger/parameters/dicomweb-common.yaml @@ -28,4 +28,19 @@ components: "image/jpeg": schema: type: string - format: byte \ No newline at end of file + format: byte + parameters: + "filter": + name: filter + description: "{attributeID}={value}; {attributeID} = {dicomTag} | {dicomKeyword} | {dicomTag}.{attributeID} | {dicomKeyword}.{attributeID}" + in: query + schema: + type: array + items: + type: string + "isRecycle": + name: isRecycle + description: "Query if the instance is in recycle bin, default is false, and notice that this one is not standard by DICOM standard (i.e. raccoon custom parameter)" + in: query + schema: + type: boolean \ No newline at end of file diff --git a/docs/swagger/parameters/mwl.yaml b/docs/swagger/parameters/mwl.yaml new file mode 100644 index 00000000..af65c85f --- /dev/null +++ b/docs/swagger/parameters/mwl.yaml @@ -0,0 +1,24 @@ +components: + parameters: + spsID: + in: path + name: spsID + required: true + schema: + type: string + spsStatus: + in: path + name: spsStatus + required: true + + schema: + type: string + enum: + - SCHEDULED + - ARRIVED + - READY + - STARTED + - DEPARTED + - CANCELED + - DISCONTINUED + - COMPLETED \ No newline at end of file diff --git a/docs/swagger/parameters/patient.yaml b/docs/swagger/parameters/patient.yaml index 5edbfcbe..5eb5f871 100644 --- a/docs/swagger/parameters/patient.yaml +++ b/docs/swagger/parameters/patient.yaml @@ -25,4 +25,12 @@ components: description: Birth time of the Patient. in: query schema: - type: string \ No newline at end of file + type: string + "patientID": + name: "patientID" + description: Patient ID + in: path + required: true + schema: + type: string + \ No newline at end of file diff --git a/docs/swagger/parameters/wado-rs.yaml b/docs/swagger/parameters/wado-rs.yaml index 290726f0..81fb985e 100644 --- a/docs/swagger/parameters/wado-rs.yaml +++ b/docs/swagger/parameters/wado-rs.yaml @@ -28,6 +28,7 @@ components: "binaryValuePath": name: "binaryValuePath" description: "{dicomTag} or {dicomTag.InlineBinary} or {dicomTag.Value.itemIndex.Tag}" - in: query + in: path + required: true schema: type: string diff --git a/error/api-errors.handler.js b/error/api-errors.handler.js new file mode 100644 index 00000000..9ca10fda --- /dev/null +++ b/error/api-errors.handler.js @@ -0,0 +1,103 @@ +const { getInternalServerErrorMessage, getNotFoundErrorMessage } = require("@root/utils/errorResponse/errorResponseMessage"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); + + +class DicomWebServiceErrorHandler { + + static doErrorResponse(response, e) { + return response.status(e.code).json({ + status: e.status, + message: e.message + }); + } +} + +class NotFoundInstanceErrorHandler { + static doErrorResponse(response, e) { + response.writeHead(404, { + "Content-Type": "application/dicom+json" + }); + return response.end(JSON.stringify(getNotFoundErrorMessage(e.message))); + } +} + +class InstanceGoneErrorHandler { + static doErrorResponse(response, e) { + response.writeHead(410, { + "Content-Type": "application/dicom+json" + }); + return response.end(JSON.stringify({ + Details: e.message, + HttpStatus: 410, + Message: "Image Gone", + Method: "GET" + })); + } +} + +class InvalidFrameNumberErrorHandler { + static doErrorResponse(response, e) { + response.writeHead(400, { + "Content-Type": "application/dicom+json" + }); + + return response.end(JSON.stringify({ + Details: e.message, + HttpStatus: 400, + Message: "Bad request", + Method: "GET" + })); + } +} + +const ApiErrorHandlerMapping = { + "DicomWebServiceError": DicomWebServiceErrorHandler, + "NotFoundInstanceError": NotFoundInstanceErrorHandler, + "InstanceGoneError": InstanceGoneErrorHandler, + "InvalidFrameNumberError": InvalidFrameNumberErrorHandler +}; + +class ApiErrorArrayHandler { + /** + * @param {import("express").Response} res + * @param {ApiLogger} apiLogger + * @param {Error} e + */ + constructor(res, apiLogger, e) { + this.response = res; + /** @type {ApiLogger} */ + this.apiLogger = apiLogger; + /** @type {Error} */ + this.error = e; + } + + doErrorResponse() { + if (this.isCustomError(this.error)) { + this.apiLogger.logger.error(this.error); + return ApiErrorHandlerMapping[this.error.name].doErrorResponse(this.response, this.error); + } else { + ApiErrorArrayHandler.raiseInternalServerError(this.error, this.response, this.apiLogger); + } + } + + /** + * + * @param {import("express").Response} response + * @param {ApiLogger} apiLogger + * @param {Error} e + */ + static raiseInternalServerError(e, response, apiLogger) { + apiLogger.logger.error(e); + + if (!response.headersSent) { + return response.status(500).set("content-type", "application/dicom+json").send(getInternalServerErrorMessage("An exception occurred")); + } + return response.end(); + } + + isCustomError(e) { + return Object.keys(ApiErrorHandlerMapping).find(key => e.name === key); + } +} + +module.exports.ApiErrorArrayHandler = ApiErrorArrayHandler; \ No newline at end of file diff --git a/error/dicom-web-service.js b/error/dicom-web-service.js index b2cd090a..1e517517 100644 --- a/error/dicom-web-service.js +++ b/error/dicom-web-service.js @@ -1,6 +1,7 @@ const DicomWebStatusCodes = { "InvalidAttributeValue": "0106", "DuplicateSOPinstance": "0111", + "NoSuchSOPInstance": "0112", "InvalidArgumentValue": "0115", "MissingAttribute": "0120", "ProcessingFailure": "0272", diff --git a/jsconfig.json b/jsconfig.json index ef54b660..13195588 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -7,7 +7,12 @@ "@models/*": ["./models/*"], "@error/*" : ["./error/*"], "@root/*": ["./*"], - "@chinlinlee/*": ["./models/DICOM/dcm4che/wrapper/org/github/chinlinlee/*"] + "@chinlinlee/*": ["./models/DICOM/dcm4che/wrapper/org/github/chinlinlee/*"], + "@dbModels/*": ["./models/mongodb/models/*"], + "@dicom-json-model": ["./models/mongodb/dicom-json-model.js"], + "@stow-rs-service": ["./api/dicom-web/controller/STOW-RS/service/stow-rs.service.js"], + "@dimse/*": ["./dimse/*"], + "@api/*": ["./api/*"] } }, "exclude": [ diff --git a/local/dicom-uploader.js b/local/dicom-uploader.js index 941de343..b564c57d 100644 --- a/local/dicom-uploader.js +++ b/local/dicom-uploader.js @@ -43,7 +43,7 @@ async function storeInstance(filePath) { host: "fake-host" }, params: {} - }, []); + }, { locals: {} }, []); /** @type {formidable.File} */ let fileObj = { diff --git a/models/DICOM/audit/auditManager.js b/models/DICOM/audit/auditManager.js index b89b8596..6c106879 100644 --- a/models/DICOM/audit/auditManager.js +++ b/models/DICOM/audit/auditManager.js @@ -2,11 +2,8 @@ const _ = require("lodash"); const { AuditMessageFactory } = require("./auditMessageFactory"); const { EventType } = require("./eventType"); - -/** - * @typedef AuditMessageModel - * @property {(json: JSON) => Promise} createMessage - */ +const { AuditMessageModel } = require("@dbModels/auditMessage"); +const { raccoonConfig } = require("@root/config-class"); class AuditManager { constructor(eventType, eventResult, @@ -152,16 +149,11 @@ class AuditManager { */ async saveToDb_(msg) { try { - await AuditManager.getAuditMessageModel().createMessage(msg); + await AuditMessageModel.createMessage(msg); } catch (e) { throw e; } } - - static getAuditMessageModel() { - const mongoose = require("mongoose"); - return mongoose.model("auditMessage"); - } } module.exports.AuditManager = AuditManager; \ No newline at end of file diff --git a/models/DICOM/audit/auditMessageFactory.js b/models/DICOM/audit/auditMessageFactory.js index d9a565ea..10d91e38 100644 --- a/models/DICOM/audit/auditMessageFactory.js +++ b/models/DICOM/audit/auditMessageFactory.js @@ -23,7 +23,10 @@ const { EventIdentificationBuilder } = require("@dcm4che/audit/EventIdentificati const { EventType } = require("./eventType"); const { default: ActiveParticipantBuilder } = require("@dcm4che/audit/ActiveParticipantBuilder"); const { AuditMessages$UserIDTypeCode } = require("@dcm4che/audit/AuditMessages$UserIDTypeCode"); + const { ParticipatingObjectFactory } = require("./participatingObjectFactory"); +const { InstanceModel } = require("@dbModels/instance.model"); + class AuditMessageFactory { constructor() { } @@ -70,7 +73,7 @@ class AuditMessageFactory { let patientParticipatingObject; for (let i = 0; i < StudyInstanceUIDs.length; i++) { let participatingObjectFactory = new ParticipatingObjectFactory( - this.getInstanceModel(), + InstanceModel, StudyInstanceUIDs[i] ); @@ -128,7 +131,7 @@ class AuditMessageFactory { let patientParticipatingObject; for (let i = 0; i < StudyInstanceUIDs.length; i++) { let participatingObjectFactory = new ParticipatingObjectFactory( - this.getInstanceModel(), + InstanceModel, StudyInstanceUIDs[i] ); @@ -195,7 +198,7 @@ class AuditMessageFactory { let patientParticipatingObject; for (let i = 0; i < studyInstanceUIDs.length; i++) { let participatingObjectFactory = new ParticipatingObjectFactory( - this.getInstanceModel(), + InstanceModel, studyInstanceUIDs[i] ); @@ -348,10 +351,6 @@ class AuditMessageFactory { return activateParticipants; } - getInstanceModel() { - const mongoose = require("mongoose"); - return mongoose.model("dicom"); - } } diff --git a/models/DICOM/dcm4che/dcm2json.js b/models/DICOM/dcm4che/dcm2json.js new file mode 100644 index 00000000..2cbdf8dd --- /dev/null +++ b/models/DICOM/dcm4che/dcm2json.js @@ -0,0 +1,45 @@ + +const path = require("path"); +const { importClassAsync } = require("java-bridge"); +const { JsonGenerator } = require("@java-wrapper/javax/json/stream/JsonGenerator"); +const { JSONWriter } = require("@dcm4che/json/JSONWriter"); +const { DicomInputStream } = require("@dcm4che/io/DicomInputStream"); +const { File } = require("@java-wrapper/java/io/File"); +const { Json } = require("@java-wrapper/javax/json/Json"); +const { Common } = require("@java-wrapper/org/github/chinlinlee/dcm777/common/Common"); +const { DicomInputStream$IncludeBulkData } = require("@dcm4che/io/DicomInputStream$IncludeBulkData"); +const { BasicBulkDataDescriptor } = require("@dcm4che/io/BasicBulkDataDescriptor"); + + +class JDcm2Json { + + /** + * + * @param {string} filename + */ + static async get(filename) { + let jFile = new File(filename); + let dicomInputStream = new DicomInputStream(jFile); + await dicomInputStream.setIncludeBulkData(DicomInputStream$IncludeBulkData.NO); + await dicomInputStream.setBulkDataDescriptor(new BasicBulkDataDescriptor()); + + let JByteArrayOutputStream = await importClassAsync("java.io.ByteArrayOutputStream"); + let byteArrayOutputStream = new JByteArrayOutputStream(); + + let jsonGeneratorFactory = await Json.createGeneratorFactory( + await Common.jsonGeneratorFactoryConfig(false) + ); + let jsonGenerator = await jsonGeneratorFactory.createGenerator(byteArrayOutputStream); + let jsonWriter = new JSONWriter(jsonGenerator); + + await dicomInputStream.setDicomInputHandler(jsonWriter); + await dicomInputStream.readDataset(); + await jsonGenerator.flush(); + + let jsonStr = await byteArrayOutputStream.toString("UTF-8"); + await dicomInputStream.close(); + return JSON.parse(jsonStr); + } +} + +module.exports.JDcm2Json = JDcm2Json; \ No newline at end of file diff --git a/models/DICOM/dcm4che/javaNode/dcm4chee/lib/qrscp/dcm777-5.29.2.jar b/models/DICOM/dcm4che/javaNode/dcm4chee/lib/qrscp/dcm777-5.29.2.jar index 6c38f0e0..9d98d135 100644 Binary files a/models/DICOM/dcm4che/javaNode/dcm4chee/lib/qrscp/dcm777-5.29.2.jar and b/models/DICOM/dcm4che/javaNode/dcm4chee/lib/qrscp/dcm777-5.29.2.jar differ diff --git a/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts b/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts index 353349fd..ce62811c 100644 --- a/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts +++ b/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts @@ -4,10 +4,12 @@ import { PresentationContext as org_dcm4che3_net_pdu_PresentationContext } from import { Dimse as org_dcm4che3_net_Dimse } from "./../../../../dcm4che3/net/Dimse"; import { Attributes as org_dcm4che3_data_Attributes } from "./../../../../dcm4che3/data/Attributes"; import { PDVInputStream as org_dcm4che3_net_PDVInputStream } from "./../../../../dcm4che3/net/PDVInputStream"; +import { QueryRetrieveLevel2 as org_dcm4che3_net_service_QueryRetrieveLevel2 } from "./../../../../dcm4che3/net/service/QueryRetrieveLevel2"; import { Long as java_lang_Long } from "./../../../../../java/lang/Long"; import { Integer as java_lang_Integer } from "./../../../../../java/lang/Integer"; import { Class as java_lang_Class } from "./../../../../../java/lang/Class"; import { CFindSCPInject as org_github_chinlinlee_dcm777_net_CFindSCPInject, CFindSCPInjectInterface as org_github_chinlinlee_dcm777_net_CFindSCPInjectInterface } from "./CFindSCPInject"; +import { EnumSet as java_util_EnumSet } from "./../../../../../java/util/EnumSet"; /** * This class just defines types, you should import {@link BasicModCFindSCP} instead of this. * This was generated by java-bridge. @@ -52,14 +54,20 @@ export declare class BasicModCFindSCPClass extends JavaClass { onDimseRQSync(var0: org_dcm4che3_net_Association | null, var1: org_dcm4che3_net_pdu_PresentationContext | null, var2: org_dcm4che3_net_Dimse | null, var3: org_dcm4che3_data_Attributes | null, var4: org_dcm4che3_net_PDVInputStream | null): void; /** * @param var0 original type: 'org.dcm4che3.net.Association' - * @return original return type: 'void' + * @param var1 original type: 'org.dcm4che3.net.pdu.PresentationContext' + * @param var2 original type: 'org.dcm4che3.data.Attributes' + * @param var3 original type: 'org.dcm4che3.data.Attributes' + * @return original return type: 'org.dcm4che3.net.service.QueryRetrieveLevel2' */ - onClose(var0: org_dcm4che3_net_Association | null): Promise; + getQrLevel(var0: org_dcm4che3_net_Association | null, var1: org_dcm4che3_net_pdu_PresentationContext | null, var2: org_dcm4che3_data_Attributes | null, var3: org_dcm4che3_data_Attributes | null): Promise; /** * @param var0 original type: 'org.dcm4che3.net.Association' - * @return original return type: 'void' + * @param var1 original type: 'org.dcm4che3.net.pdu.PresentationContext' + * @param var2 original type: 'org.dcm4che3.data.Attributes' + * @param var3 original type: 'org.dcm4che3.data.Attributes' + * @return original return type: 'org.dcm4che3.net.service.QueryRetrieveLevel2' */ - onCloseSync(var0: org_dcm4che3_net_Association | null): void; + getQrLevelSync(var0: org_dcm4che3_net_Association | null, var1: org_dcm4che3_net_pdu_PresentationContext | null, var2: org_dcm4che3_data_Attributes | null, var3: org_dcm4che3_data_Attributes | null): org_dcm4che3_net_service_QueryRetrieveLevel2 | null; /** * @return original return type: 'java.lang.String[]' */ @@ -68,6 +76,16 @@ export declare class BasicModCFindSCPClass extends JavaClass { * @return original return type: 'java.lang.String[]' */ getSOPClassesSync(): (string | null)[] | null; + /** + * @param var0 original type: 'org.dcm4che3.net.Association' + * @return original return type: 'void' + */ + onClose(var0: org_dcm4che3_net_Association | null): Promise; + /** + * @param var0 original type: 'org.dcm4che3.net.Association' + * @return original return type: 'void' + */ + onCloseSync(var0: org_dcm4che3_net_Association | null): void; /** * @param var0 original type: 'long' * @param var1 original type: 'int' @@ -153,6 +171,13 @@ export declare class BasicModCFindSCPClass extends JavaClass { * @return original return type: 'org.github.chinlinlee.dcm777.net.BasicModCFindSCP' */ static newInstanceAsync(var0: (string | null)[] | null): Promise; + /** + * @param var0 original type: 'org.github.chinlinlee.dcm777.net.CFindSCPInject' + * @param var1 original type: 'java.lang.String' + * @param var2 original type: 'java.util.EnumSet' + * @return original return type: 'org.github.chinlinlee.dcm777.net.BasicModCFindSCP' + */ + static newInstanceAsync(var0: org_github_chinlinlee_dcm777_net_CFindSCPInject | JavaInterfaceProxy | null, var1: string | null, var2: java_util_EnumSet | null): Promise; /** * @param var0 original type: 'org.github.chinlinlee.dcm777.net.CFindSCPInject' * @param var1 original type: 'java.lang.String[]' @@ -163,6 +188,12 @@ export declare class BasicModCFindSCPClass extends JavaClass { * @param var0 original type: 'java.lang.String[]' */ constructor(var0: (string | null)[] | null); + /** + * @param var0 original type: 'org.github.chinlinlee.dcm777.net.CFindSCPInject' + * @param var1 original type: 'java.lang.String' + * @param var2 original type: 'java.util.EnumSet' + */ + constructor(var0: org_github_chinlinlee_dcm777_net_CFindSCPInject | JavaInterfaceProxy | null, var1: string | null, var2: java_util_EnumSet | null); /** * @param var0 original type: 'org.github.chinlinlee.dcm777.net.CFindSCPInject' * @param var1 original type: 'java.lang.String[]' diff --git a/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts.map b/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts.map index aa4477b1..4dacfa5d 100644 --- a/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts.map +++ b/models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BasicModCFindSCP.d.ts","sourceRoot":"","sources":["../../../../../../src/java-wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,WAAW,IAAI,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACrG,OAAO,EAAE,mBAAmB,IAAI,wCAAwC,EAAE,MAAM,oDAAoD,CAAC;AACrI,OAAO,EAAE,KAAK,IAAI,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,UAAU,IAAI,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACpG,OAAO,EAAE,cAAc,IAAI,+BAA+B,EAAE,MAAM,2CAA2C,CAAC;AAC9G,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,KAAK,IAAI,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,cAAc,IAAI,+CAA+C,EAAE,uBAAuB,IAAI,wDAAwD,EAAE,MAAM,kBAAkB,CAAC;AAE1L;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,qBAAsB,SAAQ,SAAS;IAExD;;;;;;;OAOG;IACI,SAAS,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5P;;;;;;;OAOG;IACI,aAAa,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,IAAI;IACvP;;;;;;;OAOG;IACI,SAAS,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,+BAA+B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/P;;;;;;;OAOG;IACI,aAAa,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,+BAA+B,GAAG,IAAI,GAAG,IAAI;IAE1P;;;OAGG;IACI,OAAO,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAExE;;;OAGG;IACI,WAAW,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,IAAI;IAEnE;;OAEG;IACI,aAAa,IAAI,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;IAEzD;;OAEG;IACI,iBAAiB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;IAEpD;;;;OAIG;IACI,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpG;;;;OAIG;IACI,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI;IAC/F;;OAEG;IACI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAC5B;;OAEG;IACI,QAAQ,IAAI,IAAI;IACvB;;;OAGG;IACI,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAClE;;;OAGG;IACI,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;IAE7D;;;OAGG;IACI,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAE7D;;;OAGG;IACI,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO;IAExD;;OAEG;IACI,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAElC;;OAEG;IACI,YAAY,IAAI,MAAM;IAE7B;;OAEG;IACI,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAElC;;OAEG;IACI,YAAY,IAAI,MAAM;IAE7B;;OAEG;IACI,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;IAE3C;;OAEG;IACI,YAAY,IAAI,eAAe;IAEtC;;OAEG;IACI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B;;OAEG;IACI,UAAU,IAAI,IAAI;IAEzB;;OAEG;IACI,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEjC;;OAEG;IACI,aAAa,IAAI,IAAI;IAE5B;;;OAGG;WACW,gBAAgB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACzF;;;;OAIG;WACW,gBAAgB,CAAC,IAAI,EAAE,+CAA+C,GAAG,kBAAkB,CAAC,wDAAwD,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAEtO;;OAEG;gBACgB,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;IACjD;;;OAGG;gBACgB,IAAI,EAAE,+CAA+C,GAAG,kBAAkB,CAAC,wDAAwD,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;CACjM;;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,SAAQ,qBAA8F;CACnI;AACD,eAAe,gBAAgB,CAAC"} \ No newline at end of file +{"version":3,"file":"BasicModCFindSCP.d.ts","sourceRoot":"","sources":["../../../../../../src/java-wrapper/org/github/chinlinlee/dcm777/net/BasicModCFindSCP.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,WAAW,IAAI,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACrG,OAAO,EAAE,mBAAmB,IAAI,wCAAwC,EAAE,MAAM,oDAAoD,CAAC;AACrI,OAAO,EAAE,KAAK,IAAI,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,UAAU,IAAI,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACpG,OAAO,EAAE,cAAc,IAAI,+BAA+B,EAAE,MAAM,2CAA2C,CAAC;AAC9G,OAAO,EAAE,mBAAmB,IAAI,4CAA4C,EAAE,MAAM,wDAAwD,CAAC;AAC7I,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,KAAK,IAAI,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,cAAc,IAAI,+CAA+C,EAAE,uBAAuB,IAAI,wDAAwD,EAAE,MAAM,kBAAkB,CAAC;AAC1L,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAElF;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,qBAAsB,SAAQ,SAAS;IAExD;;;;;;;OAOG;IACI,SAAS,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5P;;;;;;;OAOG;IACI,aAAa,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,IAAI;IACvP;;;;;;;OAOG;IACI,SAAS,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,+BAA+B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/P;;;;;;;OAOG;IACI,aAAa,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,+BAA+B,GAAG,IAAI,GAAG,IAAI;IAE1P;;;;;;OAMG;IACI,UAAU,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,4CAA4C,GAAG,IAAI,CAAC;IAEvQ;;;;;;OAMG;IACI,cAAc,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,wCAAwC,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,EAAE,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,4CAA4C,GAAG,IAAI;IAElQ;;OAEG;IACI,aAAa,IAAI,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;IAEzD;;OAEG;IACI,iBAAiB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;IAEpD;;;OAGG;IACI,OAAO,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAExE;;;OAGG;IACI,WAAW,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,GAAG,IAAI;IAEnE;;;;OAIG;IACI,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpG;;;;OAIG;IACI,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI;IAC/F;;OAEG;IACI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAC5B;;OAEG;IACI,QAAQ,IAAI,IAAI;IACvB;;;OAGG;IACI,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAClE;;;OAGG;IACI,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;IAE7D;;;OAGG;IACI,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAE7D;;;OAGG;IACI,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO;IAExD;;OAEG;IACI,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAElC;;OAEG;IACI,YAAY,IAAI,MAAM;IAE7B;;OAEG;IACI,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAElC;;OAEG;IACI,YAAY,IAAI,MAAM;IAE7B;;OAEG;IACI,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;IAE3C;;OAEG;IACI,YAAY,IAAI,eAAe;IAEtC;;OAEG;IACI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B;;OAEG;IACI,UAAU,IAAI,IAAI;IAEzB;;OAEG;IACI,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEjC;;OAEG;IACI,aAAa,IAAI,IAAI;IAE5B;;;OAGG;WACW,gBAAgB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACzF;;;;;OAKG;WACW,gBAAgB,CAAC,IAAI,EAAE,+CAA+C,GAAG,kBAAkB,CAAC,wDAAwD,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAC3P;;;;OAIG;WACW,gBAAgB,CAAC,IAAI,EAAE,+CAA+C,GAAG,kBAAkB,CAAC,wDAAwD,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAEtO;;OAEG;gBACgB,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;IACjD;;;;OAIG;gBACgB,IAAI,EAAE,+CAA+C,GAAG,kBAAkB,CAAC,wDAAwD,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI;IACnN;;;OAGG;gBACgB,IAAI,EAAE,+CAA+C,GAAG,kBAAkB,CAAC,wDAAwD,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;CACjM;;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,SAAQ,qBAA8F;CACnI;AACD,eAAe,gBAAgB,CAAC"} \ No newline at end of file diff --git a/models/DICOM/dcmtk/index.js b/models/DICOM/dcmtk/index.js index c61c8b37..4899aaa1 100644 --- a/models/DICOM/dcmtk/index.js +++ b/models/DICOM/dcmtk/index.js @@ -3,39 +3,8 @@ const fs = require("fs"); const path = require("path"); const _ = require("lodash"); const iconv = require("iconv-lite"); -const dcmtkWinBinaryPath = "models/DICOM/dcmtk/dcmtk-3.6.5-win64-dynamic/bin"; -const dcmtkSupportTransferSyntax = [ - "1.2.840.10008.1.2", - "1.2.840.10008.1.2.1", - "1.2.840.10008.1.2.1.99", - "1.2.840.10008.1.2.2", - "1.2.840.10008.1.2.4.50", - "1.2.840.10008.1.2.4.51", - "1.2.840.10008.1.2.4.53", - "1.2.840.10008.1.2.4.55", - "1.2.840.10008.1.2.4.57", - "1.2.840.10008.1.2.4.70", - "1.2.840.10008.1.2.5" -]; - -const { dcm2json } = require("dicom-to-json"); const dcm2jsonV8 = { - exec: function (dcmfile) { - return new Promise((resolve, reject) => { - try { - dcm2json(dcmfile, function (data) { - data = data.replace(/,\\u0000/g, ""); - data = data.replace(/\\u0000/g, ""); - let obj = JSON.parse(data); - return resolve(obj); - }); - } catch (e) { - console.error(e); - return reject(e); - } - }); - }, dcmString: function (json, tag) { let data = _.get(json, tag); //console.log("d" , data); @@ -71,41 +40,6 @@ async function dcm2jpegCustomCmd(execCmd) { }); } -/** - * - * @param {string} imagesPath - * @param {number} frameNumber - * @param {Array}otherOptions - */ - async function getFrameImage (imagesPath , frameNumber ,otherOptions=[]) { - let jpegFile = imagesPath.replace(/\.dcm\b/gi , `.${frameNumber-1}.jpg`); - let execCmd = `${dcmtkWinBinaryPath}/dcmj2pnm.exe --write-jpeg "${imagesPath}" "${jpegFile}" --frame ${frameNumber} ${otherOptions.join(" ")}`; - if (process.env.OS == "linux") { - execCmd = `dcmj2pnm --write-jpeg "${imagesPath}" "${jpegFile}" --frame ${frameNumber} ${otherOptions.join(" ")}`; - } - try { - let dcm2jpegStatus = await dcm2jpegCustomCmd(execCmd.trim()); - if (dcm2jpegStatus) { - let rs = fs.createReadStream(jpegFile); - return { - status : true , - imageStream : rs, - imagePath: jpegFile - }; - } - } catch(e) { - console.error(e); - return { - status : false , - imageStream : e, - imagePath: jpegFile - }; - } -} - module.exports = { - dcm2jsonV8: dcm2jsonV8, - dcmtkSupportTransferSyntax: dcmtkSupportTransferSyntax, - dcm2jpegCustomCmd: dcm2jpegCustomCmd, - getFrameImage: getFrameImage + dcm2jsonV8: dcm2jsonV8 }; diff --git a/models/DICOM/dicom-json-model.js b/models/DICOM/dicom-json-model.js index 0b978ba9..3ebbbe25 100644 --- a/models/DICOM/dicom-json-model.js +++ b/models/DICOM/dicom-json-model.js @@ -9,9 +9,8 @@ const { } = require("./dcmtk"); const flat = require("flat"); const shortHash = require("shorthash2"); -const dicomBulkDataModel = require("../mongodb/models/dicomBulkData"); +const { DicomBulkDataModel } = require("@dbModels/dicomBulkData.model"); const { logger } = require("../../utils/logs/log"); -const patientModel = require("../mongodb/models/patient"); const { tagsNeedStore } = require("./dicom-tags-mapping"); const { raccoonConfig } = require("../../config-class"); @@ -23,7 +22,7 @@ const { dictionary } = require("./dicom-tags-dic"); * *Remove tags when processing that not use them. */ const BIG_VALUE_TAGS = [ - "52009230", + "52009230", "00480200" ]; @@ -35,7 +34,7 @@ class BaseDicomJson { */ constructor(dicomJson, ...selection) { this.dicomJson = dicomJson; - if(selection.length > 0) { + if (selection.length > 0) { this.initSelectionJson(selection); } } @@ -46,7 +45,7 @@ class BaseDicomJson { */ initSelectionJson(selection) { let selectionJson = {}; - for(let i = 0; i < selection.length; i++) { + for (let i = 0; i < selection.length; i++) { let tag = selection[i]; let item = this.getItem(tag); if (item) { @@ -60,10 +59,24 @@ class BaseDicomJson { return _.get(this.dicomJson, tag, undefined); } - getValue(tag) { + getValues(tag) { return _.get(this.dicomJson, `${tag}.Value`, undefined); } + getValue(tag) { + return _.get(this.dicomJson, `${tag}.Value.0`, undefined); + } + + /** + * + * @param {string} tag + */ + getString(tag) { + let tagSplit = tag.split("."); + let seqTag = tagSplit.join(".Value.0."); + return String(_.get(this.dicomJson, `${seqTag}.Value.0`, "")); + } + getSequenceItem(tag) { return JSONPath({ path: `$..${tag}`, @@ -72,10 +85,23 @@ class BaseDicomJson { } setValue(tag, value) { - let vrOfTag = _.get(dictionary.tagVR, `${tag}.vr`); + let lastTag = tag.split(".").at(-1); + let vrOfTag = _.get(dictionary.tagVR, `${lastTag}.vr`); _.set(this.dicomJson, `${tag}.vr`, vrOfTag); _.set(this.dicomJson, `${tag}.Value`, [value]); } + + static getKeywordOfTag(tag) { + return _.get(dictionary.tag, `${tag}`); + } + + static getTagOfKeyword(keyword) { + return _.get(dictionary.keyword, `${keyword}`); + } + + static getTagVrOfTag(tag) { + return _.get(dictionary.tagVR, `${tag}.vr`); + } } class DicomJsonModel { @@ -85,7 +111,7 @@ class DicomJsonModel { //For temp property that have big value that mongodb cannot save and cause performance issue this.tempBigTagValue = {}; - /** @type {import("../../utils/typeDef/dicom").UIDObject} */ + /** @type {import("@root/utils/typeDef/dicom").DicomUid} */ this.uidObj = {}; } @@ -113,7 +139,7 @@ class DicomJsonModel { this.dicomJson, "00080016" ), - sopInstanceUID: dcm2jsonV8.dcmString( + instanceUID: dcm2jsonV8.dcmString( this.dicomJson, "00080018" ), @@ -132,33 +158,49 @@ class DicomJsonModel { }; } + /** + * + * @param {import("@root/utils/typeDef/STOW-RS/STOW-RS").DicomFileSaveInfo} dicomFileSaveInfo + */ async storeToDb(dicomFileSaveInfo) { - let dicomJsonClone = _.cloneDeep(this.dicomJson); + let dbJson = this.getCleanDataBeforeStoringToDb(dicomFileSaveInfo); try { - let mediaStorage = this.getMediaStorageInfo(); - _.merge(dicomJsonClone, this.uidObj); - _.merge(dicomJsonClone, { - studyPath: dicomFileSaveInfo.studyPath, - seriesPath: dicomFileSaveInfo.seriesPath, - instancePath: dicomFileSaveInfo.relativePath - }); - _.merge(dicomJsonClone, mediaStorage); - _.set(dicomJsonClone, "deleteStatus", 0); - - delete dicomJsonClone.sopClass; - delete dicomJsonClone.sopInstanceUID; - await Promise.all([ - this.storeInstanceCollection(dicomJsonClone), - this.storeStudyCollection(dicomJsonClone), - this.storeSeriesCollection(dicomJsonClone), - this.storePatientCollection(dicomJsonClone) + this.storeInstanceCollection(dbJson), + this.storeStudyCollection(dbJson), + this.storeSeriesCollection(dbJson), + this.storePatientCollection(dbJson) ]); - } catch(e) { + } catch (e) { throw e; } } + /** + * + * @param {import("@root/utils/typeDef/STOW-RS/STOW-RS").DicomFileSaveInfo} dicomFileSaveInfo + * @returns {import("@root/utils/typeDef/dicom").GeneralDicomJson} + */ + getCleanDataBeforeStoringToDb(dicomFileSaveInfo) { + let dicomJsonClone = _.cloneDeep(this.dicomJson); + let mediaStorage = this.getMediaStorageInfo(); + _.merge(dicomJsonClone, this.uidObj); + _.merge(dicomJsonClone, { + studyPath: dicomFileSaveInfo.studyPath, + seriesPath: dicomFileSaveInfo.seriesPath, + instancePath: dicomFileSaveInfo.relativePath + }); + _.merge(dicomJsonClone, mediaStorage); + + delete dicomJsonClone.sopClass; + delete dicomJsonClone.instanceUID; + return dicomJsonClone; + } + + /** + * + * @param {import("@root/utils/typeDef/dicom").GeneralDicomJson} dicomJson + */ async storeInstanceCollection(dicomJson) { let query = { $and: [ @@ -169,7 +211,7 @@ class DicomJsonModel { seriesUID: this.uidObj.seriesUID }, { - instanceUID: this.uidObj.sopInstanceUID + instanceUID: this.uidObj.instanceUID } ] }; @@ -180,6 +222,10 @@ class DicomJsonModel { }); } + /** + * + * @param {import("@root/utils/typeDef/dicom").GeneralDicomJson} dicomJson + */ async storeStudyCollection(dicomJson) { await mongoose.model("dicomStudy").findOneAndUpdate( { @@ -193,6 +239,10 @@ class DicomJsonModel { ); } + /** + * + * @param {import("@root/utils/typeDef/dicom").GeneralDicomJson} dicomJson + */ async storeSeriesCollection(dicomJson) { await mongoose.model("dicomSeries").findOneAndUpdate( { @@ -213,8 +263,12 @@ class DicomJsonModel { ); } + /** + * + * @param {import("@root/utils/typeDef/dicom").GeneralDicomJson} dicomJson + */ async storePatientCollection(dicomJson) { - await patientModel.findOneAndUpdate( + await mongoose.model("patient").findOneAndUpdate( { patientID: this.uidObj.patientID }, @@ -231,6 +285,10 @@ class DicomJsonModel { ); } + /** + * + * @param {string} storeFullPath + */ async saveMetadataToFile(storeFullPath) { try { let dicomJsonClone = _.cloneDeep(this.dicomJson); @@ -259,14 +317,6 @@ class DicomJsonModel { } } - /** - * - * @param {string} tag - */ - getString(tag) { - return String(_.get(this.dicomJson, `${tag}.Value.0`, "")); - } - getMediaStorageInfo() { return { "00880130": { @@ -326,7 +376,7 @@ class DicomJsonModel { } else { startedDate = moment(startedDate, "YYYYMMDDhhmmss").toISOString(); } - + return startedDate; } @@ -340,14 +390,14 @@ class DicomJsonModel { }; } - getPatientDicomJson(dicomJson=undefined) { + getPatientDicomJson(dicomJson = undefined) { if (!dicomJson) dicomJson = this.dicomJson; let patientDicomJson = { patientID: dicomJson.patientID }; - for(let tag in tagsNeedStore.Patient) { + for (let tag in tagsNeedStore.Patient) { let value = _.get(dicomJson, tag); if (value) _.set(patientDicomJson, tag, value); @@ -356,7 +406,7 @@ class DicomJsonModel { return patientDicomJson; } - getStudyDicomJson(dicomJson=undefined) { + getStudyDicomJson(dicomJson = undefined) { if (!dicomJson) dicomJson = this.dicomJson; @@ -365,7 +415,7 @@ class DicomJsonModel { studyPath: dicomJson.studyPath }; - for(let tag in tagsNeedStore.Study) { + for (let tag in tagsNeedStore.Study) { let value = _.get(dicomJson, tag); if (value) _.set(studyDicomJson, tag, value); @@ -379,7 +429,7 @@ class DicomJsonModel { }; } - getSeriesDicomJson(dicomJson=undefined) { + getSeriesDicomJson(dicomJson = undefined) { if (!dicomJson) dicomJson = this.dicomJson; let seriesDicomJson = { @@ -388,9 +438,9 @@ class DicomJsonModel { seriesPath: dicomJson.seriesPath }; - for(let tag in tagsNeedStore.Series) { + for (let tag in tagsNeedStore.Series) { let value = _.get(dicomJson, tag); - if(value) + if (value) _.set(seriesDicomJson, tag, value); } @@ -406,7 +456,7 @@ class DicomJsonModel { class DicomJsonBinaryDataModel { - constructor(dicomJsonModel) { + constructor(dicomJsonModel, bulkDataModelClass = BulkData) { /** @type {DicomJsonModel} */ this.dicomJsonModel = dicomJsonModel; @@ -416,6 +466,8 @@ class DicomJsonBinaryDataModel { /** @type {string[]} */ this.pathGroupOfBinaryProperties = this.getPathGroupOfBinaryProperties_(); + + this.bulkDataModelClass = bulkDataModelClass; } getBinaryKeys_() { @@ -433,7 +485,7 @@ class DicomJsonBinaryDataModel { getPathGroupOfBinaryProperties_() { let pathGroupOfBinaryProperties = []; - for(let binaryKey of this.binaryKeys) { + for (let binaryKey of this.binaryKeys) { if (_.get(this.dicomJsonModel.dicomJson, `${binaryKey}.Value.0`)) { pathGroupOfBinaryProperties.push(`${binaryKey}.Value.0`); @@ -451,10 +503,10 @@ class DicomJsonBinaryDataModel { let { studyUID, seriesUID, - sopInstanceUID + instanceUID } = this.dicomJsonModel.uidObj; - for(let i = 0; i < this.binaryKeys.length ; i++) { + for (let i = 0; i < this.binaryKeys.length; i++) { let binaryKey = this.binaryKeys[i]; // Reset VR to UR, because BulkDataURI is URI @@ -469,33 +521,33 @@ class DicomJsonBinaryDataModel { _.set( this.dicomJsonModel.dicomJson, `${binaryKey}.BulkDataURI`, - `/studies/${studyUID}/series/${seriesUID}/instances/${sopInstanceUID}/bulkdata/${pathOfBinaryProperty}` + `/studies/${studyUID}/series/${seriesUID}/instances/${instanceUID}/bulkdata/${pathOfBinaryProperty}` ); - + _.unset(this.dicomJsonModel.dicomJson, `${binaryKey}.InlineBinary`); } this.dicomJsonModel.dicomJson["7FE00010"] = { vr: "UR", - BulkDataURI: `/studies/${studyUID}/series/${seriesUID}/instances/${sopInstanceUID}` + BulkDataURI: `/studies/${studyUID}/series/${seriesUID}/instances/${instanceUID}` }; } async storeAllBinaryDataToFileAndDb() { let { - sopInstanceUID + instanceUID } = this.dicomJsonModel.uidObj; - let shortInstanceUID = shortHash(sopInstanceUID); + let shortInstanceUID = shortHash(instanceUID); - - for(let i = 0; i < this.pathGroupOfBinaryProperties.length ; i++) { + + for (let i = 0; i < this.pathGroupOfBinaryProperties.length; i++) { let relativeFilename = `files/bulkData/${shortInstanceUID}/`; let pathOfBinaryProperty = this.pathGroupOfBinaryProperties[i]; let binaryData = _.get(this.dicomJsonModel.dicomJson, pathOfBinaryProperty); - if(binaryData) { + if (binaryData) { relativeFilename += `${pathOfBinaryProperty}.raw`; let filename = path.join( raccoonConfig.dicomWebConfig.storeRootPath, @@ -512,7 +564,7 @@ class DicomJsonBinaryDataModel { await fsP.writeFile(filename, Buffer.from(binaryData, "base64")); logger.info(`[STOW-RS] [Store binary data to ${filename}]`); - let bulkData = new BulkData(this.dicomJsonModel.uidObj, relativeFilename, pathOfBinaryProperty); + let bulkData = new this.bulkDataModelClass(this.dicomJsonModel.uidObj, relativeFilename, pathOfBinaryProperty); await bulkData.storeToDb(); } @@ -523,7 +575,7 @@ class DicomJsonBinaryDataModel { class BulkData { constructor(uidObj, filename, pathOfBinaryProperty) { - /** @type {import("../../utils/typeDef/dicom").UIDObject} */ + /** @type {import("@root/utils/typeDef/dicom").DicomUid} */ this.uidObj = uidObj; this.filename = filename; this.pathOfBinaryProperty = pathOfBinaryProperty; @@ -534,31 +586,22 @@ class BulkData { let item = { studyUID: this.uidObj.studyUID, seriesUID: this.uidObj.seriesUID, - instanceUID: this.uidObj.sopInstanceUID, + instanceUID: this.uidObj.instanceUID, filename: this.filename, binaryValuePath: this.pathOfBinaryProperty }; - await dicomBulkDataModel.updateOne( + await DicomBulkDataModel.createOrUpdateBulkData( { - $and: [ - { - instanceUID: this.uidObj.sopInstanceUID - }, - { - binaryValuePath: this.pathOfBinaryProperty - } - ] + instanceUID: this.uidObj.instanceUID, + binaryValuePath: this.pathOfBinaryProperty }, { studyUID: this.uidObj.studyUID, seriesUID: this.uidObj.seriesUID, - instanceUID: this.uidObj.sopInstanceUID, + instanceUID: this.uidObj.instanceUID, filename: this.filename, binaryValuePath: this.pathOfBinaryProperty - }, - { - upsert: true } ); logger.info(`[STOW-RS] [Store bulkdata ${JSON.stringify(item)} successful]`); diff --git a/models/DICOM/dicom-json-parser.js b/models/DICOM/dicom-json-parser.js index 44168550..9ec6adb2 100644 --- a/models/DICOM/dicom-json-parser.js +++ b/models/DICOM/dicom-json-parser.js @@ -1,7 +1,5 @@ const { DicomUtf8Converter } = require("./dcm4che/DicomUtf8Converter"); -const { - dcm2jsonV8 -} = require("./dcmtk"); +const { JDcm2Json } = require("./dcm4che/dcm2json"); const { logger } = require("../../utils/logs/log"); @@ -11,7 +9,7 @@ class DicomJsonParser { async parseFromFilename(filename) { let dicomJson; try { - dicomJson = await dcm2jsonV8.exec(filename); + dicomJson = await JDcm2Json.get(filename); return dicomJson; } catch (e) { @@ -26,7 +24,7 @@ class DicomJsonParser { let dicomUtf8Converter = new DicomUtf8Converter(filename); await dicomUtf8Converter.convert(); - dicomJson = await dcm2jsonV8.exec(filename); + dicomJson = await JDcm2Json.get(filename); return dicomJson; } catch(e) { throw e; diff --git a/models/DICOM/dicom-tags-mapping.js b/models/DICOM/dicom-tags-mapping.js index d889d338..273867ec 100644 --- a/models/DICOM/dicom-tags-mapping.js +++ b/models/DICOM/dicom-tags-mapping.js @@ -618,5 +618,154 @@ module.exports.tagsNeedStore = { "00741224": { "vr": "SQ" } - } + }, + MWL: { + "00080005": { + "vr": "CS" + }, + "00080050": { + "vr": "SH" + }, + "00080054": { + "vr": "AE" + }, + "00080056": { + "vr": "CS" + }, + "00080090": { + "vr": "PN" + }, + "00080201": { + "vr": "SH" + }, + "00081110": { + "vr": "SQ" + }, + "00081120": { + "vr": "SQ" + }, + "00100010": { + "vr": "PN" + }, + "00100020": { + "vr": "LO" + }, + "00100021": { + "vr": "LO" + }, + "00100024": { + "vr": "SQ" + }, + "00100030": { + "vr": "DA" + }, + "00100040": { + "vr": "CS" + }, + "00100032": { + "vr": "TM" + }, + "00101002": { + "vr": "SQ" + }, + "00101001": { + "vr": "PN" + }, + "00100033": { + "vr": "LO" + }, + "00100034": { + "vr": "LO" + }, + "00100035": { + "vr": "CS" + }, + "00100050": { + "vr": "SQ" + }, + "00100101": { + "vr": "SQ" + }, + "00100200": { + "vr": "CS" + }, + "00100212": { + "vr": "UC" + }, + "00101030": { + "vr": "DS" + }, + "00102000": { + "vr": "LO" + }, + "00102110": { + "vr": "LO" + }, + "001021C0": { + "vr": "US" + }, + "0020000D": { + "vr": "UI" + }, + "00321032": { + "vr": "PN" + }, + "00321060": { + "vr": "LO" + }, + "00321064": { + "vr": "SQ" + }, + "00380010": { + "vr": "LO" + }, + "00380050": { + "vr": "LO" + }, + "00380300": { + "vr": "LO" + }, + "00380500": { + "vr": "LO" + }, + "00400001": { + "vr": "CS" + }, + "00400002": { + "vr": "DA" + }, + "00400003": { + "vr": "TM" + }, + "00400006": { + "vr": "PN" + }, + "00400009": { + "vr": "SH" + }, + "00400010": { + "vr": "SH" + }, + "00400011": { + "vr": "SH" + }, + "00400020": { + "vr": "CS" + }, + "00400100": { + "vr": "SQ" + }, + "00401001": { + "vr": "SH" + }, + "00401003": { + "vr": "SH" + }, + "00401004": { + "vr": "LO" + }, + "00403001": { + "vr": "LO" + } + } }; diff --git a/models/FHIR/DICOM/DICOMToFHIREndpoint.js b/models/FHIR/DICOM/DICOMToFHIREndpoint.js deleted file mode 100644 index d7a1db3f..00000000 --- a/models/FHIR/DICOM/DICOMToFHIREndpoint.js +++ /dev/null @@ -1,35 +0,0 @@ -/*connectionType -dicom-wado-rs -dicom-qido-rs -dicom-stow-rs -dicon-wado-uri -direct-project -*/ - -/** - * - * @param {string} addressUrl The DICOMWeb URL of study level - * @param {*} id The unique id of this DICOMWeb URL of PACS server. - * @returns - */ -function dicomJsonToFHIREndpoint(addressUrl, id) { - let endpoint = { - resourceType: "Endpoint", - status: "active", - id: id, - connectionType: { - system: "http://terminology.hl7.org/CodeSystem/endpoint-connection-type", - code: "dicom-wado-rs" - }, - payloadType: [ - { - text: "DICOM" - } - ], - payloadMimeType: ["application/dicom"], - address: addressUrl - }; - return endpoint; -} - -module.exports.dicomJsonToFHIREndpoint = dicomJsonToFHIREndpoint; diff --git a/models/FHIR/DICOM/DICOMToFHIRImagingStudy.js b/models/FHIR/DICOM/DICOMToFHIRImagingStudy.js deleted file mode 100644 index c08bcf88..00000000 --- a/models/FHIR/DICOM/DICOMToFHIRImagingStudy.js +++ /dev/null @@ -1,364 +0,0 @@ -const dicomParser = require("dicom-parser"); -const moment = require("moment"); -const flatten = require("flat"); -const _ = require("lodash"); -const { dcm2jsonV8 } = require("../../DICOM/dcmtk"); - -/** - * 將Class轉成Json的父類別 - */ -class ToJsonParent { - constructor() {} - toJson() { - return Object.getOwnPropertyNames(this).reduce((a, b) => { - if (this[b]) a[b] = this[b]; - return a; - }, {}); - } -} - -/** - * ImagingStudy類別 - */ -class ImagingStudy extends ToJsonParent { - constructor() { - super(); - this.resourceType = "ImagingStudy"; - this.id = ""; - this.identifier = []; //0..* - this.status = "unknown"; //1..1 code registered | available | cancelled | entered-in-error | unknown - this.modality = undefined; //0..* coding - this.subject = new Reference(); //1..1 reference - this.started = undefined; //0..1 dateTime - this.endpoint = undefined; //0..* Reference don't have this now (This is mean where the DicomWEB server) - this.numberOfSeries = undefined; //0..1 int - this.numberOfInstances = undefined; //0..1 int - this.description = undefined; //0..1 string - this.series = []; //0..* 放置ImagingStudy_Series - } -} - -class ImagingStudy_Series extends ToJsonParent { - constructor() { - super(); - this.uid = ""; //1..1 - this.number = undefined; //0..1 int - this.modality = new Coding(); //1..1 coding // - this.modality.system = "http://dicom.nema.org/resources/ontology/DCM"; - this.description = undefined; //0..1 string - this.numberOfInstances = ""; //0..1 int - this.endpoint = undefined; //0..* Reference - this.bodySite = undefined; //0..1 Coding - this.laterality = undefined; - this.started = undefined; //0..1 dateTime - this.performer = undefined; //0..* {function (Codeable) :0..1, actor:1..1 (Reference)} - this.instance = []; //0..* - } -} - -class ImagingStudy_Series_Instance extends ToJsonParent { - constructor() { - super(); - this.uid = ""; //1..1 - this.sopClass = new Coding(); //1..1 coding - this.number = ""; //0..1 - this.title = undefined; //0..1 - } -} - -class Coding { - constructor() { - this.system = undefined; - this.version = undefined; - this.code = undefined; - this.display = undefined; - this.userSelected = undefined; - } -} - -class Identifier { - constructor() { - this.use = undefined; - this.type = undefined; - this.system = undefined; - this.value = undefined; - this.period = undefined; - } -} - -class Reference { - constructor() { - this.reference = undefined; //(Literal reference, Relative, internal or absolute URL)(string) - this.type = undefined; //string - this.identifier = undefined; - this.display = undefined; - } -} - -class CodeableConcept { - constructor() { - this.Coding = []; - this.text = undefined; - } -} - -class Period { - constructor() { - this.start = undefined; - this.end = undefined; - } -} - -/** - * - * @param {string} filename The filename of DICOM. - * @return {JSON} - */ -function dicomFileToFHIRImagingStudy(filename) { - try { - let dataset = dicomParser.parseDicom(filename); - let studyobj = new ImagingStudy(); - let studyInstanceUID = dataset.string("x0020000d"); - let ANandIssuer = getDicomParserTagStringConcat( - dataset, - "x00080050", - "x00080051" - ); - studyobj.id = studyInstanceUID; - let identifiers = [ - studyInstanceUID, - ANandIssuer, - dataset.string("x00200010") - ]; - studyobj.identifier = getStudyIdentifiers(identifiers); - studyobj.modality = dataset.string("x00080061"); - let patientId = dataset.string("x00100020"); - if (patientId) { - studyobj.subject.reference = - "Patient/" + dataset.string("x00100020"); - studyobj.subject.type = "Patient"; - studyobj.subject.identifier.use = "usual"; - studyobj.subject.identifier.value = dataset.string("x00100020"); - } else { - studyobj.subject.reference = "Patient/unknown"; - studyobj.subject.type = "Patient"; - studyobj.subject.identifier.use = "anonymous"; - studyobj.subject.identifier.value = "unknown"; - } - - let imaging_started = - dataset.string("x00080020") + dataset.string("x00080030"); - const date = moment(imaging_started, "YYYYMMDDhhmmss").toISOString(); - studyobj.started = date; - studyobj.numberOfSeries = dataset.string("x00201206"); - studyobj.numberOfInstances = dataset.string("x00201208"); - studyobj.description = dataset.string("x00081030"); - let study_series_obj = new ImagingStudy_Series(); - study_series_obj.uid = dataset.string("x0020000e"); - study_series_obj.number = dataset.intString("x00200011"); - study_series_obj.modality.code = dataset.string("x00080060"); - study_series_obj.description = dataset.string("x0008103e"); - study_series_obj.numberOfInstances = dataset.intString("x00201209"); - study_series_obj.bodySite.display = dataset.string("x00180015"); - let series_started = - dataset.string("x00080021") + dataset.string("x00080031"); - const series_date = moment(series_started, "YYYYMMDDhhmmss").toDate(); - study_series_obj.started = - series_date != null ? series_date : undefined; - study_series_obj.performer = - dataset.string("x00081050") || - dataset.string("x00081052") || - dataset.string("x00081070") || - dataset.string("x00081072"); - let study_series_insatance_obj = new ImagingStudy_Series_Instance(); - study_series_insatance_obj.uid = dataset.string("x00080018"); - study_series_insatance_obj.sopClass.system = "urn:ietf:rfc:3986"; - study_series_insatance_obj.sopClass.code = - "urn:oid:" + dataset.string("x00080016"); - study_series_insatance_obj.number = dataset.intString("x00200013"); - study_series_insatance_obj.title = - dataset.string("x00080008") || - dataset.string("x00070080") || - (dataset.string("x0040a043") != undefined - ? dataset.string("x0040a043") + dataset.string("x00080104") - : undefined) || - dataset.string("x00420010"); - let imagingStudyJson = combineImagingStudyClass( - studyobj, - study_series_obj, - study_series_insatance_obj - ); - return imagingStudyJson; - } catch (e) { - console.error(e); - throw e; - } -} - -/** - * - * @param {JSON} dicomJson - * @return {JSON} - */ -function dicomJsonToFHIRImagingStudy(dicomJson) { - //#region study - let studyObj = new ImagingStudy(); - let studyInstanceUID = dcm2jsonV8.dcmString(dicomJson, "0020000D"); - - let ANandIssuer = getTagStringConcat(dicomJson, "00080050", "00080051"); - studyObj.id = studyInstanceUID; - let identifiers = [ - studyInstanceUID, - ANandIssuer, - dcm2jsonV8.dcmString(dicomJson, "00200010") - ]; - studyObj.identifier = getStudyIdentifiers(identifiers); - - let patientId = dcm2jsonV8.dcmString(dicomJson, "00100020"); - studyObj.subject.identifier = new Identifier(); - if (patientId) { - studyObj.subject.reference = - "Patient/" + dcm2jsonV8.dcmString(dicomJson, "00100020"); - studyObj.subject.type = "Patient"; - studyObj.subject.identifier.use = "usual"; - studyObj.subject.identifier.value = dcm2jsonV8.dcmString( - dicomJson, - "00100020" - ); - } else { - studyObj.subject.reference = "Patient/unknown"; - studyObj.subject.type = "Patient"; - studyObj.subject.identifier.use = "anonymous"; - studyObj.subject.identifier.value = "unknown"; - } - - let studyStartedStr = - dcm2jsonV8.dcmString(dicomJson, "00080020") + - dcm2jsonV8.dcmString(dicomJson, "00080030"); - studyObj.started = moment(studyStartedStr, "YYYYMMDDhhmmss").toISOString(); - studyObj.numberOfSeries = dcm2jsonV8.dcmString(dicomJson, "00201206"); - studyObj.numberOfInstances = dcm2jsonV8.dcmString(dicomJson, "00201208"); - studyObj.description = dcm2jsonV8.dcmString(dicomJson, "00081030"); - //#endregion - - //#region series - let seriesObj = new ImagingStudy_Series(); - seriesObj.uid = dcm2jsonV8.dcmString(dicomJson, "0020000E"); - seriesObj.number = dcm2jsonV8.dcmString(dicomJson, "00200011"); - seriesObj.modality.code = dcm2jsonV8.dcmString(dicomJson, "00080060"); - seriesObj.description = dcm2jsonV8.dcmString(dicomJson, "0008103E"); - seriesObj.numberOfInstances = dcm2jsonV8.dcmString(dicomJson, "00201209"); - seriesObj.bodySite = new Coding(); - seriesObj.bodySite.display = dcm2jsonV8.dcmString(dicomJson, "00180015"); - - let seriesStarted = - dcm2jsonV8.dcmString(dicomJson, "00080021") + - dcm2jsonV8.dcmString(dicomJson, "00080031"); - seriesObj.started = moment(seriesStarted, "YYYYMMDDhhmmss"); - seriesObj.started = seriesObj.started.isValid() - ? seriesObj.started.toISOString() - : undefined; - seriesObj.performer = - dcm2jsonV8.dcmString(dicomJson, "00081050") || - dcm2jsonV8.dcmString(dicomJson, "00081052") || - dcm2jsonV8.dcmString(dicomJson, "00081070") || - dcm2jsonV8.dcmString(dicomJson, "00081072"); - //#endregion - - //#region instance - - let instanceObj = new ImagingStudy_Series_Instance(); - instanceObj.uid = dcm2jsonV8.dcmString(dicomJson, "00080018"); - instanceObj.sopClass.system = "urn:ietf:rfc:3986"; - instanceObj.sopClass.code = - "urn:oid:" + dcm2jsonV8.dcmString(dicomJson, "00080016"); - instanceObj.number = dcm2jsonV8.dcmString(dicomJson, "00200013"); - instanceObj.title = - dcm2jsonV8.dcmString(dicomJson, "00080008") || - dcm2jsonV8.dcmString(dicomJson, "00070080") || - (dcm2jsonV8.dcmString(dicomJson, "0040a043") != undefined - ? dcm2jsonV8.dcmString(dicomJson, "0040a043") + - dcm2jsonV8.dcmString(dicomJson, "00080104") - : undefined) || - dcm2jsonV8.dcmString(dicomJson, "00420010"); - //#endregion - - let imagingStudyJson = combineImagingStudyClass( - studyObj, - seriesObj, - instanceObj - ); - return imagingStudyJson; -} - -function getTagStringConcat(dicomJson, ...tags) { - let result = ""; - for (let i = 0; i < tags.length; i++) { - let tag = tags[i]; - let tagValue = dcm2jsonV8.dcmString(dicomJson, tag); - if (tagValue) result += tagValue; - } - if (!result) return undefined; - return result; -} - -function getDicomParserTagStringConcat(dataSet, ...tags) { - let result = ""; - for (let i = 0; i < tags.length; i++) { - let tag = tags[i]; - let tagValue = dataSet.string(tag); - if (tagValue) result += tagValue; - } - if (!result) return undefined; - return result; -} - -function getStudyIdentifiers(identifiers) { - let result = []; - if (identifiers[0] != undefined) { - let identifier1 = new Identifier(); - identifier1.use = "official"; - identifier1.system = "urn:dicom:uid"; - identifier1.value = "urn:oid:" + identifiers[0]; - result.push(identifier1); - } - //need sample dicom with the organization - if (identifiers[1] != undefined) { - let identifier2 = new Identifier(); - identifier2.type = new Coding(); - identifier2.use = "usual"; - identifier2.value = identifiers[1]; - result.push(identifier2); - } - if (identifiers[2] != undefined) { - let identifier3 = new Identifier(); - identifier3.use = "secondary"; - identifier3.value = "s" + identifiers[2]; - result.push(identifier3); - } - return result; -} - -function combineImagingStudyClass( - imagingStudy, - imagingStudySeries, - imagingStudySeriesInstance -) { - try { - let imagingStudyJson = imagingStudy.toJson(); - let seriesJson = imagingStudySeries.toJson(); - let instanceJson = imagingStudySeriesInstance.toJson(); - seriesJson.instance.push(instanceJson); - imagingStudyJson.series.push(seriesJson); - let flattenData = flatten(imagingStudyJson); - flattenData = _.pickBy(flattenData, _.identity); - imagingStudyJson = flatten.unflatten(flattenData); - return imagingStudyJson; - } catch (e) { - console.error(e); - return false; - } -} - -module.exports.dicomJsonToFHIRImagingStudy = dicomJsonToFHIRImagingStudy; -module.exports.dicomFileToFHIRImagingStudy = dicomFileToFHIRImagingStudy; diff --git a/models/FHIR/DICOM/DICOMToFHIRPatient.js b/models/FHIR/DICOM/DICOMToFHIRPatient.js deleted file mode 100644 index fd6bd03c..00000000 --- a/models/FHIR/DICOM/DICOMToFHIRPatient.js +++ /dev/null @@ -1,202 +0,0 @@ -const dicomParser = require("dicom-parser"); -const uuid = require("uuid"); -const fsP = require("node:fs/promises"); -const _ = require("lodash"); -const moment = require("moment"); -const { dcm2jsonV8 } = require("../../DICOM/dcmtk"); - -class HumanName { - constructor() { - this.use = "anonymous"; - this.text = undefined; - this.family = undefined; //姓氏 - this.given = undefined; //名字或中間名 - this.prefix = undefined; - this.suffix = undefined; - } - toJson() { - return Object.getOwnPropertyNames(this).reduce((a, b) => { - if (this[b]) a[b] = this[b]; - return a; - }, {}); - } -} - -/** - * - * @param {string} filename The filename of the DICOM. - */ -async function dicomFileToFHIRPatient(filename) { - try { - let dicomFileBuffer = await fsP.readFile(filename); - let dataset = dicomParser.parseDicom(dicomFileBuffer); - let pName = dataset.string("x00100010"); - let pGender = dataset.string("x00100040") || "unknown"; - let FHIRGender = { - M: "male", - F: "female", - O: "other", - UNKNOWN: "unknown" - }; - pGender = FHIRGender[pGender.toUpperCase()]; - let pBD = dataset.string("x00100030"); - let patientName = new HumanName(); - if (pName == undefined) { - pName = "UNKNOWN"; - } else { - patientName.use = "usual"; - } - patientName.text = pName; - let DICOMpName = _.pickBy(dicomParser.parsePN(pName), _.identity); //remove undefined or null key - - patientName = patientName.toJson(); - let pJson = JSON.stringify(patientName); - pJson = JSON.parse(pJson); - let FHIRpName = { - familyName: (pJson) => { - pJson.family = DICOMpName.familyName; - }, - givenName: (pJson) => { - if (pJson.given) { - pJson.given.push(DICOMpName.givenName); - } else { - pJson.given = []; - pJson.given.push(DICOMpName.givenName); - } - }, - middleName: (pJson) => { - if (pJson.given) { - pJson.given.push(DICOMpName.middleName); - } else { - pJson.given = []; - pJson.given.push(DICOMpName.middleName); - } - }, - prefix: (pJson) => { - if (pJson.prefix) { - pJson.prefix.push(DICOMpName.middleName); - } else { - pJson.prefix = []; - pJson.prefix.push(DICOMpName.middleName); - } - }, - suffix: (pJson) => { - if (pJson.prefix) { - pJson.prefix.push(DICOMpName.middleName); - } else { - pJson.prefix = []; - pJson.prefix.push(DICOMpName.middleName); - } - } - }; - for (let key in DICOMpName) { - FHIRpName[key](pJson); - } - let Patient = { - resourceType: "Patient", - id: dataset.string("x00100020") || uuid.v4(), - gender: pGender, - active: true, - name: [pJson] - }; - if (pBD) { - Patient.birthDate = moment.utc(pBD).format("YYYY-MM-DD"); - } - Patient.id = Patient.id.replace(/[\s\u0000]/gim, ""); - Patient.id = Patient.id.replace(/_/gim, ""); - return Patient; - } catch (e) { - console.error(e); - throw e; - } -} - -/** - * - * @param {JSON} dcmJson - * @returns - */ -function dicomJsonToFHIRPatient(dcmJson) { - let pName = dcm2jsonV8.dcmString(dcmJson, "00100010"); - let pGender = dcm2jsonV8.dcmString(dcmJson, "00100040") || "unknown"; - let FHIRGender = { - M: "male", - F: "female", - O: "other", - UNKNOWN: "unknown" - }; - pGender = FHIRGender[pGender.toUpperCase()]; - let pBD = dcm2jsonV8.dcmString(dcmJson, "00100030"); - let patientName = new HumanName(); - if (pName == undefined) { - pName = {}; - _.set(pName, "Alphabetic", "UNKNOWN"); - } else { - patientName.use = "usual"; - } - patientName.text = pName.Alphabetic; - let DICOMpName = _.pickBy( - dicomParser.parsePN(pName.Alphabetic), - _.identity - ); //remove undefined or null key - - patientName = patientName.toJson(); - let pJson = JSON.stringify(patientName); - pJson = JSON.parse(pJson); - let FHIRpName = { - familyName: (pJson) => { - pJson.family = DICOMpName.familyName; - }, - givenName: (pJson) => { - if (pJson.given) { - pJson.given.push(DICOMpName.givenName); - } else { - pJson.given = []; - pJson.given.push(DICOMpName.givenName); - } - }, - middleName: (pJson) => { - if (pJson.given) { - pJson.given.push(DICOMpName.middleName); - } else { - pJson.given = []; - pJson.given.push(DICOMpName.middleName); - } - }, - prefix: (pJson) => { - if (pJson.prefix) { - pJson.prefix.push(DICOMpName.middleName); - } else { - pJson.prefix = []; - pJson.prefix.push(DICOMpName.middleName); - } - }, - suffix: (pJson) => { - if (pJson.prefix) { - pJson.prefix.push(DICOMpName.middleName); - } else { - pJson.prefix = []; - pJson.prefix.push(DICOMpName.middleName); - } - } - }; - for (let key in DICOMpName) { - FHIRpName[key](pJson); - } - let Patient = { - resourceType: "Patient", - id: dcm2jsonV8.dcmString(dcmJson, "00100020") || uuid.v4(), - gender: pGender, - active: true, - name: [pJson] - }; - Patient.id = Patient.id.replace(/[\s\u0000]/gim, ""); - Patient.id = Patient.id.replace(/_/gim, ""); - if (pBD) { - Patient.birthDate = moment.utc(pBD).format("YYYY-MM-DD"); - } - return Patient; -} - -module.exports.dicomJsonToFHIRPatient = dicomJsonToFHIRPatient; -module.exports.dicomFileToFHIRPatient = dicomFileToFHIRPatient; diff --git a/models/db/auditMessage.loggerImpl.js b/models/db/auditMessage.loggerImpl.js new file mode 100644 index 00000000..22d94eb1 --- /dev/null +++ b/models/db/auditMessage.loggerImpl.js @@ -0,0 +1,9 @@ +const { logger } = require("@root/utils/logs/log"); + +class AuditMessageModelLoggerDbImpl { + static async createMessage(msg) { + logger.info(JSON.stringify(msg)); + } +} + +module.exports.AuditMessageModel = AuditMessageModelLoggerDbImpl; \ No newline at end of file diff --git a/models/mongodb/connector.js b/models/mongodb/connector.js index aeea0b96..69d52fab 100644 --- a/models/mongodb/connector.js +++ b/models/mongodb/connector.js @@ -14,7 +14,7 @@ const { authSource, isShardingMode, urlOptions -} = raccoonConfig.mongoDbConfig; +} = raccoonConfig.dbConfig; module.exports = exports = function () { const collection = {}; @@ -85,7 +85,7 @@ function getCollections(dirname, collectionObj) { file.slice(-3) === ".js" ); for (let file of jsFilesInDir) { - const moduleName = file.split(".")[0]; + const moduleName = file.split(".js")[0]; console.log("moduleName :: ", moduleName); console.log("path : ", __dirname + dirname); collectionObj[moduleName] = require(__dirname + diff --git a/models/mongodb/convertQuery.js b/models/mongodb/convertQuery.js new file mode 100644 index 00000000..dc175754 --- /dev/null +++ b/models/mongodb/convertQuery.js @@ -0,0 +1,133 @@ +const _ = require("lodash"); + +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { timeQuery, mongoDateQuery } = require("./service"); + +function checkIsOr(value, keyName) { + if (_.isObject(value) && _.get(value[keyName], "$or")) { + return true; + } + return false; +} + +/** + * convert value that contains comma to $or query of MongoDB + * @param {string} iKey + * @param {string} iValue + */ +function commaValue(iKey, iValue) { + let $or = []; + iValue = iValue.split(","); + for (let i = 0; i < iValue.length; i++) { + let obj = {}; + obj[iKey] = iValue[i]; + $or.push(obj); + } + return $or; +} + +/** + * + * @param {string} value + * @returns + */ +function getWildCardQuery(value) { + let wildCardIndex = value.indexOf("*"); + let questionIndex = value.indexOf("?"); + + if (wildCardIndex >= 0 || questionIndex >= 0) { + value = value.replace(/\*/gm, ".*"); + value = value.replace(/\?/gm, "."); + value = value.replace(/\^/gm, "\\^"); + value = "^" + value; + return new RegExp(value, "gm"); + } + + return value; +} + +/** + * + * convert all request query object to to $or query and push to $and query + * @param {Object} iQuery + * @returns + */ +async function convertRequestQueryToMongoQuery(iQuery) { + let queryKey = Object.keys(iQuery); + let mongoQs = { + $match: { + $and: [] + } + }; + for (let i = 0; i < queryKey.length; i++) { + let mongoOrs = { + $or: [] + }; + let nowKey = queryKey[i]; + let value = commaValue(nowKey, iQuery[nowKey]); + for (let x = 0; x < value.length; x++) { + let nowValue = value[x][nowKey]; + value[x][nowKey] = getWildCardQuery(nowValue); + + try { + let keySplit = nowKey.split("."); + let tag = keySplit[keySplit.length - 2]; + let vrOfTag = dictionary.tagVR[tag]; + await vrQueryLookup[vrOfTag.vr](value[x], nowKey); + } catch (e) { + if (!(e instanceof TypeError)) console.error(e); + } + + if (checkIsOr(value[x], nowKey)) { + mongoOrs.$or.push(..._.get(value[x][nowKey], "$or")); + } else { + mongoOrs.$or.push(value[x]); + } + } + mongoQs.$match.$and.push(mongoOrs); + } + return mongoQs.$match.$and.length == 0 + ? { + $match: {} + } + : mongoQs; +} + +const vrQueryLookup = { + DA: async (value, tag) => { + let q = await mongoDateQuery(value, tag, false); + }, + DT: async (value, tag) => { + let q = await mongoDateQuery(value, tag, false, "YYYYMMDDhhmmss.SSSSSSZZ"); + }, + PN: async (value, tag) => { + let queryValue = _.cloneDeep(value[tag]); + value[tag] = { + $or: [ + { + [`${tag}.Alphabetic`]: queryValue + }, + { + [`${tag}.familyName`]: queryValue + }, + { + [`${tag}.givenName`]: queryValue + }, + { + [`${tag}.middleName`]: queryValue + }, + { + [`${tag}.prefix`]: queryValue + }, + { + [`${tag}.suffix`]: queryValue + } + ] + }; + }, + TM: async (value, tag) => { + value[tag] = timeQuery(value, tag); + } +}; + +module.exports.convertRequestQueryToMongoQuery = convertRequestQueryToMongoQuery; \ No newline at end of file diff --git a/models/mongodb/deleteSchedule.js b/models/mongodb/deleteSchedule.js index ee6ce2f5..74003a54 100644 --- a/models/mongodb/deleteSchedule.js +++ b/models/mongodb/deleteSchedule.js @@ -1,12 +1,16 @@ const schedule = require("node-schedule"); const moment = require("moment"); const { logger } = require("@root/utils/logs/log"); -const dicomStudyModel = require("./models/dicomStudy"); -const dicomModel = require("./models/dicom"); -const dicomSeriesModel = require("./models/dicomSeries"); +const { StudyModel } = require("@dbModels/study.model"); +const { InstanceModel } = require("@dbModels/instance.model"); +const { SeriesModel } = require("@dbModels/series.model"); +const { PatientModel } = require("./models/patient.model"); // Delete dicom with delete status >= 2 schedule.scheduleJob("*/5 * * * * *", async function () { + deleteExpirePatients().catch(e => { + logger.error(e); + }); deleteExpireStudies().catch((e) => { logger.error(e); }); @@ -18,9 +22,38 @@ schedule.scheduleJob("*/5 * * * * *", async function () { }); }); +async function deleteExpirePatients() { + let deletedPatients = await PatientModel.find({ + deleteStatus: { + $gte: 2 + } + }); + + for (let deletedPatient of deletedPatients) { + let updateAtDate = moment(deletedPatient.updatedAt); + let now = moment(); + let diff = now.diff(updateAtDate, "seconds"); + if (diff >= 30) { + let patientID = deletedPatient.patientID; + logger.info("delete expired patient: " + patientID); + await Promise.all([ + StudyModel.deleteMany({ + "00100020.Value.0": patientID + }), + InstanceModel.deleteMany({ + "00100020.Value.0": patientID + }), + SeriesModel.deleteMany({ + "00100020.Value.0": patientID + }), + deletedPatient.delete() + ]); + } + } +} async function deleteExpireStudies() { - let deletedStudies = await dicomStudyModel.find({ + let deletedStudies = await StudyModel.find({ deleteStatus: { $gte: 2 } @@ -35,22 +68,22 @@ async function deleteExpireStudies() { logger.info("delete expired study: " + studyUID); await Promise.all([ - dicomModel.deleteMany({ + InstanceModel.deleteMany({ studyUID }), - dicomSeriesModel.deleteMany({ + SeriesModel.deleteMany({ studyUID }), deletedStudy.delete() ]); - await deletedStudy.deleteStudyFolder(); + await deletedStudy.deleteDicomInstances(); } } } async function deleteExpireSeries() { - let deletedSeries = await dicomSeriesModel.find({ + let deletedSeries = await SeriesModel.find({ deleteStatus: { $gte: 2 } @@ -61,11 +94,11 @@ async function deleteExpireSeries() { let now = moment(); let diff = now.diff(updateAtDate, "seconds"); if (diff >= 30) { - let {studyUID, seriesUID} = aDeletedSeries; + let { studyUID, seriesUID } = aDeletedSeries; logger.info("delete expired series: " + seriesUID); await Promise.all([ - dicomModel.deleteMany({ + InstanceModel.deleteMany({ $and: [ { x0020000D: studyUID }, { x0020000E: seriesUID } @@ -74,28 +107,28 @@ async function deleteExpireSeries() { aDeletedSeries.delete() ]); - await aDeletedSeries.deleteSeriesFolder(); + await aDeletedSeries.deleteDicomInstances(); } } } async function deleteExpireInstances() { - let deletedInstances = await dicomModel.find({ + let deletedInstances = await InstanceModel.find({ deleteStatus: { $gte: 2 } }); for (let deletedInstance of deletedInstances) { - let {instanceUID} = deletedInstance; + let { instanceUID } = deletedInstance; let updateAtDate = moment(deletedInstance.updatedAt); let now = moment(); let diff = now.diff(updateAtDate, "days"); if (diff >= 30) { logger.info("delete expired instance: " + instanceUID); - await deletedInstance.deleteInstance(); await deletedInstance.delete(); + await deletedInstance.deleteDicomInstances(); } } } \ No newline at end of file diff --git a/models/mongodb/models/auditMessage.js b/models/mongodb/models/auditMessage.js index 0d2681e0..973698fb 100644 --- a/models/mongodb/models/auditMessage.js +++ b/models/mongodb/models/auditMessage.js @@ -23,4 +23,5 @@ let auditMessage = new mongoose.Schema( const auditMessageModel = mongoose.model("auditMessage", auditMessage, "auditMessage"); -module.exports = auditMessageModel; \ No newline at end of file +module.exports = auditMessageModel; +module.exports.AuditMessageModel = auditMessageModel; \ No newline at end of file diff --git a/models/mongodb/models/dicomBulkData.js b/models/mongodb/models/dicomBulkData.js deleted file mode 100644 index 59230537..00000000 --- a/models/mongodb/models/dicomBulkData.js +++ /dev/null @@ -1,40 +0,0 @@ -const mongoose = require("mongoose"); - -let dicomBulkDataSchema = new mongoose.Schema( - { - studyUID: { - type: String, - default: void 0, - index: true - }, - seriesUID: { - type: String, - default: void 0, - index: true - }, - instanceUID: { - type: String, - default: void 0, - index: true - }, - filename: { - type: String, - default: void 0 - }, - binaryValuePath: { - type: String, - default: void 0 - } - }, - { - strict: false, - versionKey: false - } -); - -let dicomBulkData = mongoose.model( - "dicomBulkData", - dicomBulkDataSchema, - "dicomBulkData" -); -module.exports = dicomBulkData; diff --git a/models/mongodb/models/dicomBulkData.model.js b/models/mongodb/models/dicomBulkData.model.js new file mode 100644 index 00000000..f7efddb7 --- /dev/null +++ b/models/mongodb/models/dicomBulkData.model.js @@ -0,0 +1,166 @@ +const mongoose = require("mongoose"); + +let dicomBulkDataSchema = new mongoose.Schema( + { + studyUID: { + type: String, + default: void 0, + index: true + }, + seriesUID: { + type: String, + default: void 0, + index: true + }, + instanceUID: { + type: String, + default: void 0, + index: true + }, + filename: { + type: String, + default: void 0 + }, + binaryValuePath: { + type: String, + default: void 0 + } + }, + { + strict: false, + versionKey: false, + statics: { + /** + * + * @param {Object} query + * @param {string} query.studyUID + * @param {string} query.seriesUID + * @param {string} query.instanceUID + * @param {string} query.binaryValuePath + * @param {any} options + * @returns + */ + findOneBulkData: async function(query, options) { + return await mongoose.model("dicomBulkData").findOne(query, options).exec(); + }, + /** + * + * @param {Object} query + * @param {string} query.studyUID + * @param {any} options + * @returns + */ + findStudyBulkData: async function(query, options) { + return await mongoose.model("dicomBulkData").find({ + $and: [ + { + studyUID: query.studyUID + } + ] + }, options?.projection, options?.options).exec(); + }, + /** + * + * @param {Object} query + * @param {string} query.studyUID + * @param {string} query.seriesUID + * @param {*} options + * @returns + */ + findSeriesBulkData: async function(query, options) { + return await mongoose.model("dicomBulkData").find({ + $and: [ + { + studyUID: query.studyUID + }, + { + seriesUID: query.seriesUID + } + ] + }, options?.projection, options?.options).exec(); + }, + /** + * + * @param {Object} query + * @param {string} query.studyUID + * @param {string} query.seriesUID + * @param {string} query.instanceUID + * @param {*} options + * @returns + */ + findInstanceBulkData: async function(query, options) { + return await mongoose.model("dicomBulkData").find({ + $and: [ + { + studyUID: query.studyUID + }, + { + seriesUID: query.seriesUID + }, + { + instanceUID: query.instanceUID + } + ] + }, options?.projection, options?.options).exec(); + }, + /** + * + * @param {Object} query + * @param {string} query.studyUID + * @param {string} query.seriesUID + * @param {string} query.instanceUID + * @param {string} query.binaryValuePath + * @param {*} options + * @returns + */ + findSpecificBulkData: async function(query, options) { + return await mongoose.model("dicomBulkData").findOne({ + $and: [ + { + studyUID: query.studyUID + }, + { + seriesUID: query.seriesUID + }, + { + instanceUID: query.instanceUID + }, + { + binaryValuePath: { + $regex: `^${query.binaryValuePath}`, + $options: "m" + } + } + ] + }, options?.projection, options?.options).exec(); + }, + createOrUpdateBulkData: async function(query, newBulkData, options) { + await mongoose.model("dicomBulkData").updateOne( + { + $and: [ + { + instanceUID: query.instanceUID + }, + { + binaryValuePath: query.binaryValuePath + } + ] + }, + newBulkData, + { + upsert: true + } + ); + } + } + } +); + +let dicomBulkData = mongoose.model( + "dicomBulkData", + dicomBulkDataSchema, + "dicomBulkData" +); + +module.exports = dicomBulkData; +module.exports.DicomBulkDataModel = dicomBulkData; diff --git a/models/mongodb/models/dicomSeries.js b/models/mongodb/models/dicomSeries.js deleted file mode 100644 index 39f81f60..00000000 --- a/models/mongodb/models/dicomSeries.js +++ /dev/null @@ -1,230 +0,0 @@ -const fsP = require("fs/promises"); -const path = require("path"); -const mongoose = require("mongoose"); -const _ = require("lodash"); -const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); -const { getVRSchema } = require("../schema/dicomJsonAttribute"); -const { getStoreDicomFullPathGroup, IncludeFieldsFactory } = require("../service"); -const { dictionary } = require("@models/DICOM/dicom-tags-dic"); -const { raccoonConfig } = require("@root/config-class"); -const { logger } = require("@root/utils/logs/log"); - -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} - -let dicomSeriesSchema = new mongoose.Schema( - { - studyUID: { - type: String, - default: void 0, - index: true, - required: true - }, - seriesUID: { - type: String, - default: void 0, - index: true, - required: true - }, - seriesPath: { - type: String, - default: void 0 - }, - deleteStatus: { - type: Number, - default: 0 - } - }, - { - strict: true, - versionKey: false, - toObject: { - getters: true - }, - methods: { - async incrementDeleteStatus() { - this.deleteStatus = this.deleteStatus + 1; - await this.save(); - }, - async deleteSeriesFolder() { - let seriesPath = this.seriesPath; - logger.warn("Permanently delete series folder: " + seriesPath); - await fsP.rm(path.join(raccoonConfig.dicomWebConfig.storeRootPath, seriesPath), { - force: true, - recursive: true - }); - }, - getAttributes: async function() { - let series = this.toObject(); - delete series._id; - delete series.id; - - let jsonStr = JSON.stringify(series); - return await Common.getAttributesFromJsonString(jsonStr); - } - }, - statics: { - getDimseResultCursor: async function (query, keys) { - return mongoose.model("dicomSeries").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - } - }, - timestamps: true - } -); - -for (let tag in tagsNeedStore.Study) { - let vr = tagsNeedStore.Study[tag].vr; - let tagSchema = getVRSchema(vr); - dicomSeriesSchema.add({ - [tag]: tagSchema - }); -} - -for (let tag in tagsNeedStore.Series) { - let vr = tagsNeedStore.Series[tag].vr; - let tagSchema = getVRSchema(vr); - dicomSeriesSchema.add({ - [tag]: tagSchema - }); -} - -dicomSeriesSchema.index({ - studyUID: 1 -}); -dicomSeriesSchema.index({ - seriesUID: 1 -}); -dicomSeriesSchema.index({ - "0020000D": 1 -}); -dicomSeriesSchema.index({ - "0020000E": 1 -}); - - -/** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -dicomSeriesSchema.statics.getDicomJson = async function (queryOptions) { - let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); - let seriesFields = includeFieldsFactory.getSeriesLevelFields(); - - try { - let docs = await mongoose - .model("dicomSeries") - .find({ - ...queryOptions.query, - deleteStatus: { - $eq: 0 - } - }, { - ...seriesFields - }) - .setOptions({ - strictQuery: false - }) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .exec(); - - - let seriesDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - obj["00081190"] = { - vr: "UR", - Value: [ - `${queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}` - ] - }; - - _.set(obj, dictionary.keyword.RetrieveAETitle, { - ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], - Value: [raccoonConfig.aeTitle] - }); - - return obj; - }); - - return seriesDicomJson; - - } catch (e) { - throw e; - } -}; - -/** - * - * @param {object} iParam - * @param {string} iParam.studyUID - * @param {string} iParam.seriesUID - */ -dicomSeriesSchema.statics.getPathGroupOfInstances = async function (iParam) { - let { studyUID, seriesUID } = iParam; - try { - let query = [ - { - $match: { - $and: [ - { - seriesUID: seriesUID - }, - { - studyUID: studyUID - } - ] - } - }, - { - $group: { - _id: "$seriesUID", - pathList: { - $addToSet: { - studyUID: "$studyUID", - seriesUID: "$seriesUID", - instanceUID: "$instanceUID", - instancePath: "$instancePath" - } - } - } - } - ]; - let docs = await mongoose.model("dicom").aggregate(query); - let pathGroup = _.get(docs, "0.pathList", []); - - let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); - - return fullPathGroup; - } catch (e) { - throw e; - } -}; - - -/** - * @typedef { mongoose.Model & { - * getPathGroupOfInstances: function(iParam: { - * studyUID: string, - * seriesUID: string - * }): Promise; - * getDicomJson: function(queryOptions: import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions): Promise; - * }} DicomSeriesModel -*/ - -/** @type { DicomSeriesModel } */ -let dicomSeriesModel = mongoose.model( - "dicomSeries", - dicomSeriesSchema, - "dicomSeries" -); - -module.exports = dicomSeriesModel; diff --git a/models/mongodb/models/dicomStudy.js b/models/mongodb/models/dicomStudy.js deleted file mode 100644 index e4e4deb3..00000000 --- a/models/mongodb/models/dicomStudy.js +++ /dev/null @@ -1,199 +0,0 @@ -const fsP = require("fs/promises"); -const path = require("path"); -const mongoose = require("mongoose"); -const _ = require("lodash"); -const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); -const { getVRSchema } = require("../schema/dicomJsonAttribute"); -const { - getStoreDicomFullPathGroup, - IncludeFieldsFactory -} = require("../service"); -const { - tagsOfRequiredMatching -} = require("../../DICOM/dicom-tags-mapping"); -const { raccoonConfig } = require("../../../config-class"); -const { dictionary } = require("@models/DICOM/dicom-tags-dic"); -const { logger } = require("@root/utils/logs/log"); - -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} - -let dicomStudySchema = new mongoose.Schema( - { - studyUID: { - type: String, - default: void 0, - index: true, - required: true - }, - studyPath: { - type: String, - default: void 0 - }, - deleteStatus: { - type: Number, - default: 0 - } - }, - { - strict: true, - versionKey: false, - toObject: { - getters: true - }, - methods: { - async incrementDeleteStatus() { - this.deleteStatus = this.deleteStatus + 1; - await this.save(); - }, - async deleteStudyFolder() { - let studyPath = this.studyPath; - logger.warn("Permanently delete study folder: " + studyPath); - await fsP.rm(path.join(raccoonConfig.dicomWebConfig.storeRootPath, studyPath), { - force: true, - recursive: true - }); - }, - getAttributes: async function() { - let study = this.toObject(); - delete study._id; - delete study.id; - - let jsonStr = JSON.stringify(study); - return await Common.getAttributesFromJsonString(jsonStr); - } - }, - statics: { - getDimseResultCursor: async function (query, keys) { - return mongoose.model("dicomStudy").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - } - }, - timestamps: true - } -); - -for (let tag in tagsNeedStore.Study) { - let vr = tagsNeedStore.Study[tag].vr; - let tagSchema = getVRSchema(vr); - dicomStudySchema.add({ - [tag]: tagSchema - }); -} - -dicomStudySchema.index({ - studyUID: 1 -}); -dicomStudySchema.index({ - "0020000D": 1 -}); - -/** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -dicomStudySchema.statics.getDicomJson = async function (queryOptions) { - let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); - let studyFields = includeFieldsFactory.getStudyLevelFields(); - - try { - let docs = await mongoose.model("dicomStudy").find({ - ...queryOptions.query, - deleteStatus: { - $eq: 0 - } - }, studyFields) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .setOptions({ - strictQuery: false - }) - .exec(); - - let studyDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - obj["00081190"] = { - vr: "UR", - Value: [`${queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}`] - }; - - _.set(obj, dictionary.keyword.RetrieveAETitle, { - ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], - Value: [raccoonConfig.aeTitle] - }); - - return obj; - }); - - return studyDicomJson; - - } catch (e) { - throw e; - } -}; - -/** - * - * @param {Object} iParam - * @param {string} iParam.studyUID - */ -dicomStudySchema.statics.getPathGroupOfInstances = async function (iParam) { - let { studyUID } = iParam; - try { - let query = [ - { - $match: { - studyUID: studyUID - } - }, - { - $group: { - _id: "$studyUID", - pathList: { - $addToSet: { - studyUID: "$studyUID", - seriesUID: "$seriesUID", - instanceUID: "$instanceUID", - instancePath: "$instancePath" - } - } - } - } - ]; - let docs = await mongoose.model("dicom").aggregate(query).exec(); - let pathGroup = _.get(docs, "0.pathList", []); - - let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); - - return fullPathGroup; - - } catch (e) { - throw e; - } -}; - -/** - * @typedef { mongoose.Model & { - * getPathGroupOfInstances: function(iParam: { - * studyUID: string, - * }): Promise; - * getDicomJson: function(queryOptions: import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions): Promise - * }} DicomStudyModel -*/ - -/** @type { DicomStudyModel } */ -let dicomStudyModel = mongoose.model( - "dicomStudy", - dicomStudySchema, - "dicomStudy" -); - -module.exports = dicomStudyModel; diff --git a/models/mongodb/models/dicomToJpegTask.js b/models/mongodb/models/dicomToJpegTask.model.js similarity index 96% rename from models/mongodb/models/dicomToJpegTask.js rename to models/mongodb/models/dicomToJpegTask.model.js index cbc5272f..03dd309e 100644 --- a/models/mongodb/models/dicomToJpegTask.js +++ b/models/mongodb/models/dicomToJpegTask.model.js @@ -58,4 +58,5 @@ async function insertOrUpdate(item) { } module.exports = dicomToJpegTask; +module.exports.DicomToJpegTaskModel = dicomToJpegTask; module.exports.insertOrUpdate = insertOrUpdate; diff --git a/models/mongodb/models/dicom.js b/models/mongodb/models/instance.model.js similarity index 65% rename from models/mongodb/models/dicom.js rename to models/mongodb/models/instance.model.js index a2cf0df7..ca2455b8 100644 --- a/models/mongodb/models/dicom.js +++ b/models/mongodb/models/instance.model.js @@ -12,12 +12,9 @@ const { getStoreDicomFullPath, IncludeFieldsFactory } = require("../service"); const { logger } = require("../../../utils/logs/log"); const { raccoonConfig } = require("@root/config-class"); const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { DicomSchemaOptionsFactory, InstanceDocDicomJsonHandler } = require("../schema/dicom.schema"); +const notImageSOPClass = require("@models/DICOM/dicomWEB/notImageSOPClass"); -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} let verifyingObserverSchema = new mongoose.Schema( { @@ -32,6 +29,208 @@ let verifyingObserverSchema = new mongoose.Schema( } ); +let dicomSchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("instance", InstanceDocDicomJsonHandler), + { + strict: false, + methods: { + deleteDicomInstances: async function () { + let instancePath = this.instancePath; + try { + logger.warn("Permanently delete instance: " + instancePath); + + await fsP.unlink( + path.join( + raccoonConfig.dicomWebConfig.storeRootPath, + instancePath + ) + ); + } catch (e) { + console.error(e); + } + } + }, + statics: { + findOneByDicomUID: async function ({ studyUID, seriesUID, instanceUID }) { + return await mongoose.model("dicom").findOne({ studyUID, seriesUID, instanceUID }).exec(); + }, + getAuditInstancesInfoFromStudyUID: async (studyUID) => { + let instances = await mongoose.model("dicom").find({ studyUID }).exec(); + + let instanceInfos = { + sopClassUIDs: [], + accessionNumbers: [], + patientID: "", + patientName: "" + }; + + for (let instance of instances) { + let sopClassUID = _.get(instance, "00080016.Value.0"); + let accessionNumber = _.get(instance, "00080050.Value.0"); + let patientID = _.get(instance, "00100020.Value.0"); + let patientName = _.get(instance, "00100010.Value.0.Alphabetic"); + sopClassUID ? instanceInfos.sopClassUIDs.push(sopClassUID) : null; + accessionNumber ? instanceInfos.accessionNumbers.push(accessionNumber) : null; + patientID ? instanceInfos.patientID = patientID : null; + patientName ? instanceInfos.patientName = patientName : null; + } + + instanceInfos.sopClassUIDs = _.uniq(instanceInfos.sopClassUIDs); + instanceInfos.accessionNumbers = _.uniq(instanceInfos.accessionNumbers); + + return instanceInfos; + }, + getDicomJsonProjection: function (queryOptions) { + let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); + return includeFieldsFactory.getInstanceLevelFields(); + }, + /** + * + * @param {object} iParam + * @param {string} iParam.studyUID + * @param {string} iParam.seriesUID + * @param {string} iParam.instanceUID + */ + getPathOfInstance: async function (iParam) { + try { + + let doc = await mongoose.model("dicom").findOne(mongoose.model("dicom").getPathGroupQuery(iParam), { + studyUID: 1, + seriesUID: 1, + instanceUID: 1, + instancePath: 1 + }).exec(); + + if (doc) { + let docObj = doc.toObject(); + docObj.instancePath = getStoreDicomFullPath(docObj); + + return docObj; + } + + return undefined; + + } catch (e) { + throw e; + } + }, + getPathGroupQuery: function (iParam) { + let { studyUID, seriesUID, instanceUID } = iParam; + return { + $and: [ + { + studyUID: studyUID + }, + { + seriesUID: seriesUID + }, + { + instanceUID: instanceUID + }, + { + deleteStatus: { + $eq: 0 + } + } + ] + }; + }, + /** + * + * @param {string} studyUID + * @param {string} seriesUID + */ + getInstanceOfMedianIndex: async function (query) { + let instanceCountOfStudy = await mongoose.model("dicom").countDocuments({ + $and: [ + { + studyUID: query.studyUID + }, + { + deleteStatus: { + $eq: 0 + } + } + ] + }); + + return await mongoose.model("dicom").findOne(query, { + studyUID: 1, + seriesUID: 1, + instanceUID: 1, + instancePath: 1 + }) + .sort({ + studyUID: 1, + seriesUID: 1 + }) + .skip(instanceCountOfStudy >> 1) + .limit(1) + .exec(); + }, + /** + * + * @param {object} iParam + * @param {string} iParam.studyUID + * @param {string} iParam.seriesUID + * @param {string} iParam.instanceUID + * @returns { Promise | Promise } + */ + getInstanceFrame: async function (iParam) { + let { studyUID, seriesUID, instanceUID } = iParam; + + try { + /** @type { import("mongoose").FilterQuery } */ + let query = { + $and: [ + { + studyUID: studyUID + }, + { + seriesUID: seriesUID + }, + { + instanceUID: instanceUID + }, + { + "00080016.Value": { + $nin: notImageSOPClass + } + }, + { + deleteStatus: { + $eq: 0 + } + } + ] + }; + let doc = await mongoose.model("dicom").findOne(query, { + studyUID: 1, + seriesUID: 1, + instanceUID: 1, + instancePath: 1, + "00280008": 1, //number of frames + "00020010": 1, //transfer syntax UID + "00281050": 1, // Window Center + "00281051": 1 // Window Width + }).exec(); + if (doc) { + let docObj = doc.toObject(); + docObj.instancePath = path.join( + raccoonConfig.dicomWebConfig.storeRootPath, + docObj.instancePath + ); + return docObj; + } + return undefined; + } catch (e) { + throw e; + } + } + } + } +); + /** * @constructs dicomModelSchema */ @@ -126,77 +325,7 @@ let dicomModelSchema = new mongoose.Schema( Value: [mongoose.SchemaTypes.String] } }, - { - strict: false, - versionKey: false, - toObject: { - getters: true - }, - methods: { - async incrementDeleteStatus() { - this.deleteStatus = this.deleteStatus + 1; - await this.save(); - }, - async deleteInstance() { - let instancePath = this.instancePath; - try { - logger.warn("Permanently delete instance: " + instancePath); - - await fsP.unlink( - path.join( - raccoonConfig.dicomWebConfig.storeRootPath, - instancePath - ) - ); - } catch (e) { - console.error(e); - } - }, - getAttributes: async function() { - let study = this.toObject(); - delete study._id; - delete study.id; - - let jsonStr = JSON.stringify(study); - return await Common.getAttributesFromJsonString(jsonStr); - } - }, - statics: { - getDimseResultCursor: async (query, keys) => { - return mongoose.model("dicom").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - }, - getAuditInstancesInfoFromStudyUID: async (studyUID) => { - let instances = await mongoose.model("dicom").find({studyUID}).exec(); - - let instanceInfos = { - sopClassUIDs: [], - accessionNumbers: [], - patientID: "", - patientName: "" - }; - - for (let instance of instances) { - let sopClassUID = _.get(instance, "00080016.Value.0"); - let accessionNumber = _.get(instance, "00080050.Value.0"); - let patientID = _.get(instance, "00100020.Value.0"); - let patientName = _.get(instance, "00100010.Value.0.Alphabetic"); - sopClassUID ? instanceInfos.sopClassUIDs.push(sopClassUID) : null; - accessionNumber ? instanceInfos.accessionNumbers.push(accessionNumber) : null; - patientID ? instanceInfos.patientID = patientID : null; - patientName ? instanceInfos.patientName = patientName : null; - } - - instanceInfos.sopClassUIDs = _.uniq(instanceInfos.sopClassUIDs); - instanceInfos.accessionNumbers = _.uniq(instanceInfos.accessionNumbers); - - return instanceInfos; - } - }, - timestamps: true - } + dicomSchemaOptions ); dicomModelSchema.index({ @@ -405,163 +534,9 @@ async function updateStudyNumberOfStudyRelatedInstance(doc) { } } -/** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -dicomModelSchema.statics.getDicomJson = async function (queryOptions) { - - let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); - let instanceFields = includeFieldsFactory.getInstanceLevelFields(); - - try { - - let docs = await mongoose - .model("dicom") - .find({ - ...queryOptions.query, - deleteStatus: { - $eq: 0 - } - }, { - ...instanceFields - }) - .setOptions({ - strictQuery: false - }) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .exec(); - - let instanceDicomJson = docs.map(v => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - obj["00081190"] = { - vr: "UR", - Value: [ - `${queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}/instances/${obj["00080018"]["Value"][0]}` - ] - }; - - _.set(obj, dictionary.keyword.RetrieveAETitle, { - ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], - Value: [raccoonConfig.aeTitle] - }); - - return obj; - }); - - return instanceDicomJson; - - } catch (e) { - throw e; - } - -}; - -/** - * - * @param {object} iParam - * @param {string} iParam.studyUID - * @param {string} iParam.seriesUID - * @param {string} iParam.instanceUID - */ -dicomModelSchema.statics.getPathOfInstance = async function (iParam) { - let { studyUID, seriesUID, instanceUID } = iParam; - - try { - let query = { - $and: [ - { - studyUID: studyUID - }, - { - seriesUID: seriesUID - }, - { - instanceUID: instanceUID - }, - { - deleteStatus: { - $eq: 0 - } - } - ] - }; - - let doc = await mongoose.model("dicom").findOne(query, { - studyUID: 1, - seriesUID: 1, - instanceUID: 1, - instancePath: 1 - }).exec(); - - if (doc) { - let docObj = doc.toObject(); - docObj.instancePath = getStoreDicomFullPath(docObj); - - return docObj; - } - - return undefined; - - } catch (e) { - throw e; - } -}; - -/** - * - * @param {string} studyUID - * @param {string} seriesUID - */ -dicomModelSchema.statics.getInstanceOfMedianIndex = async function (query) { - let instanceCountOfStudy = await mongoose.model("dicom").countDocuments({ - $and: [ - { - studyUID: query.studyUID - }, - { - deleteStatus: { - $eq: 0 - } - } - ] - }); - - return await mongoose.model("dicom").findOne(query, { - studyUID: 1, - seriesUID: 1, - instanceUID: 1, - instancePath: 1 - }) - .sort({ - studyUID: 1, - seriesUID: 1 - }) - .skip(instanceCountOfStudy >> 1) - .limit(1) - .exec(); -}; - -/** - * @typedef {import("mongoose").Model & { - * getPathOfInstance: (iParam: { - * studyUID: string, - * seriesUID: string, - * instanceUID: string - * }) => Promise; - * getDicomJson: (queryOptions: import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions) => Promise; - * getInstanceOfMedianIndex: (studyUID: string, seriesUID: string) => Promise; - * }} DicomModelSchema - * - */ - let dicomModel = mongoose.model("dicom", dicomModelSchema, "dicom"); -/** @type {DicomModelSchema} */ module.exports = dicomModel; +module.exports.InstanceModel = dicomModel; module.exports.getModalitiesInStudy = getModalitiesInStudy; diff --git a/models/mongodb/models/mwlitems.model.js b/models/mongodb/models/mwlitems.model.js new file mode 100644 index 00000000..e9b20bb6 --- /dev/null +++ b/models/mongodb/models/mwlitems.model.js @@ -0,0 +1,169 @@ +const path = require("path"); +const mongoose = require("mongoose"); +const _ = require("lodash"); +const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); +const { getVRSchema } = require("../schema/dicomJsonAttribute"); +const { IncludeFieldsFactory } = require("../service"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { raccoonConfig } = require("@root/config-class"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); +const { convertRequestQueryToMongoQuery } = require("../convertQuery"); + +let Common; +if (raccoonConfig.dicomDimseConfig.enableDimse) { + require("@models/DICOM/dcm4che/java-instance"); + Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; +} + + +let mwlItemSchema = new mongoose.Schema( + {}, + { + strict: true, + versionKey: false, + toObject: { + getters: true + }, + statics: { + getDimseResultCursor: async function (query, keys) { + return mongoose.model("mwlItems").find(query, keys).setOptions({ + strictQuery: false + }) + .cursor(); + }, + /** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ + getDicomJson: async function (queryOptions) { + let projection = mongoose.model("mwlItems").getDicomJsonProjection(queryOptions.includeFields); + try { + let docs = await mongoose.model("mwlItems").find(queryOptions.query, projection) + .limit(queryOptions.limit) + .skip(queryOptions.skip) + .setOptions({ + strictQuery: false + }) + .exec(); + + + let mwlDicomJson = await Promise.all(docs.map(async (v) => await v.toGeneralDicomJson())); + + return mwlDicomJson; + + } catch (e) { + throw e; + } + }, + getDicomJsonProjection: function (includeFields) { + let includeFieldsFactory = new IncludeFieldsFactory(includeFields); + return includeFieldsFactory.getMwlLevelFields(); + }, + getCount: async function (query) { + let mongoQuery = await convertRequestQueryToMongoQuery(query); + return await mongoose.model("mwlItems").countDocuments(mongoQuery); + }, + deleteByStudyInstanceUIDAndSpsID: async function (studyUID, spsID) { + return await mongoose.model("mwlItems").deleteMany({ + $and: [ + { + [`${dictionary.keyword.StudyInstanceUID}.Value.0`]: studyUID + }, + { + [`${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepID}.Value.0`]: spsID + } + ] + }); + }, + /** + * + * @param {string} studyUID + * @param {string} spsID + */ + findOneByStudyInstanceUIDAndSpsID: async function (studyUID, spsID) { + return await mongoose.model("mwlItems").findOne({ + $and: [ + { + [`${dictionary.keyword.StudyInstanceUID}.Value.0`]: studyUID + }, + { + [`${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepID}.Value.0`]: spsID + } + ] + }); + }, + createWithGeneralDicomJson: async function (generalDicomJson) { + let mwlItemModelObj = new mongoose.model("mwlItems")(generalDicomJson); + return await mwlItemModelObj.save(); + }, + updateOneWithGeneralDicomJson: async function (mwlItem, generalDicomJson) { + mwlItem.$set({ + ...generalDicomJson + }); + return await mwlItem.save(); + }, + updateStatusByQuery: async function (query, status) { + let mongoQuery = (await convertRequestQueryToMongoQuery(query)).$match; + let updateResult = await mongoose.model("mwlItems").updateMany(mongoQuery, { + $set: { + [`${dictionary.keyword.ScheduledProcedureStepSequence.tag}.Value.0.${dictionary.keyword.ScheduledProcedureStepStatus.tag}.Value.0`]: status + } + }).exec(); + return updateResult.modifiedCount; + }, + findMwlItems: async function (query) { + let mongoQuery = await convertRequestQueryToMongoQuery(query); + return await mongoose.model("mwlItems").find(mongoQuery); + } + }, + methods: { + toGeneralDicomJson: async function () { + let obj = this.toObject(); + delete obj._id; + delete obj.id; + return obj; + }, + toDicomJson: async function () { + return new BaseDicomJson(await this.toGeneralDicomJson()); + }, + getAttributes: async function () { + let jsonStr = JSON.stringify(this.toDicomJson()); + return await Common.getAttributesFromJsonString(jsonStr); + }, + /** + * + * @param {"SCHEDULED" | "ARRIVED" | "READY" | "STARTED" | "DEPARTED" | "CANCELED" | "DISCONTINUED" | "COMPLETED"} status + */ + updateStatus: async function (status) { + this.$set(`${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepStatus}.Value.0`, status); + await this.save(); + } + } + } +); + +for (let tag in tagsNeedStore.MWL) { + let vr = tagsNeedStore.MWL[tag].vr; + let tagSchema = getVRSchema(vr); + mwlItemSchema.add({ + [tag]: tagSchema + }); +} + +for (let tag in tagsNeedStore.Patient) { + let vr = tagsNeedStore.Patient[tag].vr; + let tagSchema = getVRSchema(vr); + mwlItemSchema.add({ + [tag]: tagSchema + }); +} + +let mwlItemModel = mongoose.model( + "mwlItems", + mwlItemSchema, + "mwlItems" +); + +module.exports = mwlItemModel; +module.exports.MwlItemModel = mwlItemModel; diff --git a/models/mongodb/models/patient.js b/models/mongodb/models/patient.js deleted file mode 100644 index 5dc47b09..00000000 --- a/models/mongodb/models/patient.js +++ /dev/null @@ -1,174 +0,0 @@ -const path = require("path"); -const mongoose = require("mongoose"); -const _ = require("lodash"); -const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); -const { getVRSchema } = require("../schema/dicomJsonAttribute"); -const { getStoreDicomFullPathGroup } = require("../service"); -const { - tagsOfRequiredMatching -} = require("../../DICOM/dicom-tags-mapping"); -const { raccoonConfig } = require("@root/config-class"); - -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} - - -let patientSchema = new mongoose.Schema( - { - patientID: { - type: String, - default: void 0, - index: true, - required: true - }, - studyPaths: { - type: [String], - default: void 0 - } - }, - { - strict: true, - versionKey: false, - toObject: { - getters: true - }, - statics: { - getDimseResultCursor : async function (query, keys) { - return mongoose.model("patient").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - } - }, - methods: { - getAttributes: async function() { - let patient = this.toObject(); - delete patient._id; - delete patient.id; - - let jsonStr = JSON.stringify(patient); - return await Common.getAttributesFromJsonString(jsonStr); - } - } - } -); - -for (let tag in tagsNeedStore.Patient) { - let vr = tagsNeedStore.Patient[tag].vr; - let tagSchema = getVRSchema(vr); - patientSchema.add({ - [tag]: tagSchema - }); -} - -// Index patient id -patientSchema.index({ - patientID: 1 -}); -patientSchema.index({ - "00100020": 1 -}); - -/** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -patientSchema.statics.getDicomJson = async function (queryOptions) { - let patientFields = getPatientLevelFields(); - - try { - let docs = await mongoose.model("patient").find(queryOptions.query, patientFields) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .setOptions({ - strictQuery: false - }) - .exec(); - - - let patientDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - return obj; - }); - - return patientDicomJson; - - } catch (e) { - throw e; - } -}; - -function getPatientLevelFields() { - let fields = {}; - for (let tag in tagsOfRequiredMatching.Patient) { - fields[tag] = 1; - } - return fields; -} - -/** - * - * @param {Object} iParam - * @param {string} iParam.studyUID - */ -patientSchema.statics.getPathGroupOfInstances = async function (iParam) { - let { patientID } = iParam; - try { - let query = [ - { - $match: { - "00100020.Value": patientID - } - }, - { - $group: { - _id: "$studyUID", - pathList: { - $addToSet: { - studyUID: "$studyUID", - seriesUID: "$seriesUID", - instanceUID: "$instanceUID", - instancePath: "$instancePath" - } - } - } - } - ]; - let docs = await mongoose.model("dicom").aggregate(query).exec(); - let pathGroup = _.get(docs, "0.pathList", []); - - let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); - - return fullPathGroup; - - } catch (e) { - throw e; - } -}; - -/** - * @typedef { mongoose.Model & { - * getPathGroupOfInstances: function(iParam: { - * patientID: string - * }): Promise; - * getDicomJson: function(queryOptions: DicomJsonMongoQueryOptions): Promise - * }} PatientModel -*/ - -/** @type {PatientModel} */ -let patientModel = mongoose.model( - "patient", - patientSchema, - "patient" -); - -/** @type {PatientModel} */ -module.exports = patientModel; - -module.exports.getPatientLevelFields = getPatientLevelFields; diff --git a/models/mongodb/models/patient.model.js b/models/mongodb/models/patient.model.js new file mode 100644 index 00000000..3c29d878 --- /dev/null +++ b/models/mongodb/models/patient.model.js @@ -0,0 +1,162 @@ +const mongoose = require("mongoose"); +const _ = require("lodash"); +const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); +const { getVRSchema } = require("../schema/dicomJsonAttribute"); +const { + tagsOfRequiredMatching +} = require("../../DICOM/dicom-tags-mapping"); +const { raccoonConfig } = require("@root/config-class"); +const { DicomSchemaOptionsFactory, PatientDocDicomJsonHandler } = require("../schema/dicom.schema"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); + +let patientSchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("patient", PatientDocDicomJsonHandler), + { + methods: { + toGeneralDicomJson: async function () { + let obj = this.toObject(); + delete obj._id; + delete obj.id; + delete obj.patientID; + delete obj.studyPaths; + delete obj.deleteStatus; + delete obj.createdAt; + delete obj.updatedAt; + + return obj; + }, + toDicomJson: async function() { + return new BaseDicomJson(await this.toGeneralDicomJson()); + } + }, + statics: { + getPathGroupQuery: function (iParam) { + let { patientID } = iParam; + return { + $match: { + "00100020.Value": patientID + } + }; + }, + /** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ + getDicomJsonProjection: function (queryOptions) { + let fields = {}; + for (let tag in tagsOfRequiredMatching.Patient) { + fields[tag] = 1; + } + return fields; + }, + /** + * + * @param {string} patientId + * @param {any} patient + */ + findOneOrCreatePatient: async function(patientId, patient) { + /** @type {PatientModel | null} */ + let foundPatient = await mongoose.model("patient").findOne({ + "00100020.Value": patientId + }); + + if (!foundPatient) { + /** @type {PatientModel} */ + let patientObj = new mongoose.model("patient")(patient); + patient = await patientObj.save(); + } + + return patient; + }, + findOneByPatientID: async function(patientID) { + return await mongoose.model("patient").findOne({ + patientID + }); + }, + /** + * + * @param {string} patientID + * @param {any} patient patient general dicom json + */ + createOrUpdatePatient: async function(patientID, patient) { + return await mongoose.model("patient").findOneAndUpdate({ + patientID + }, patient, { upsert: true, new: true }); + }, + /** + * + * @param {string} patientID + */ + getCountByPatientID: async function(patientID) { + return await mongoose.model("patient").countDocuments({ + patientID + }); + } + } + } +); + +let patientSchema = new mongoose.Schema( + { + patientID: { + type: String, + default: void 0, + index: true, + required: true + }, + studyPaths: { + type: [String], + default: void 0 + }, + deleteStatus: { + type: Number, + default: 0 + } + }, + patientSchemaOptions +); + +for (let tag in tagsNeedStore.Patient) { + let vr = tagsNeedStore.Patient[tag].vr; + let tagSchema = getVRSchema(vr); + patientSchema.add({ + [tag]: tagSchema + }); +} + +// default storage media file set info from config +patientSchema[dictionary.keyword.StorageMediaFileSetID] = { + ...patientSchema[dictionary.keyword.StorageMediaFileSetID], + default: { + vr: "SH", + Value: raccoonConfig.mediaStorageID + } +}; + +patientSchema[dictionary.keyword.StorageMediaFileSetUID] = { + ...patientSchema[dictionary.keyword.StorageMediaFileSetUID], + default: { + vr: "UI", + Value: raccoonConfig.mediaStorageUID + } +}; + +// Index patient id +patientSchema.index({ + patientID: 1 +}); +patientSchema.index({ + "00100020": 1 +}); + +let patientModel = mongoose.model( + "patient", + patientSchema, + "patient" +); + +module.exports = patientModel; + +module.exports.PatientModel = patientModel; diff --git a/models/mongodb/models/series.model.js b/models/mongodb/models/series.model.js new file mode 100644 index 00000000..4b3501d4 --- /dev/null +++ b/models/mongodb/models/series.model.js @@ -0,0 +1,124 @@ +const fsP = require("fs/promises"); +const path = require("path"); +const mongoose = require("mongoose"); +const _ = require("lodash"); +const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); +const { getVRSchema } = require("../schema/dicomJsonAttribute"); +const { IncludeFieldsFactory } = require("../service"); +const { raccoonConfig } = require("@root/config-class"); +const { logger } = require("@root/utils/logs/log"); +const { BaseDicomSchemaDef, DicomSchemaOptionsFactory, SeriesDocDicomJsonHandler } = require("../schema/dicom.schema"); + +let dicomSeriesSchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("series", SeriesDocDicomJsonHandler), + { + methods: { + incrementDeleteStatus: async function () { + await mongoose.model("dicom").updateMany({ + seriesUID: this.seriesUID + }, { + $inc: { + deleteStatus: 1 + } + }); + this.deleteStatus += 1; + await this.save(); + }, + deleteDicomInstances: async function () { + let seriesPath = this.seriesPath; + logger.warn("Permanently delete series folder: " + seriesPath); + await fsP.rm(path.join(raccoonConfig.dicomWebConfig.storeRootPath, seriesPath), { + force: true, + recursive: true + }); + } + }, + statics: { + findOneByDicomUID: async function({ studyUID, seriesUID }) { + return await mongoose.model("dicomSeries").findOne({ studyUID, seriesUID }).exec(); + }, + /** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ + getDicomJsonProjection: function (queryOptions) { + let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); + return includeFieldsFactory.getSeriesLevelFields(); + }, + getPathGroupQuery: function (iParam) { + let { studyUID, seriesUID } = iParam; + return { + $match: { + $and: [ + { + seriesUID: seriesUID + }, + { + studyUID: studyUID + } + ] + } + }; + } + } + } +); + +let dicomSeriesSchema = new mongoose.Schema( + { + ...BaseDicomSchemaDef + }, + dicomSeriesSchemaOptions +); + +dicomSeriesSchema.add({ + seriesUID: { + type: String, + default: void 0, + index: true, + required: true + }, + seriesPath: { + type: String, + default: void 0 + } +}); + +for (let tag in tagsNeedStore.Study) { + let vr = tagsNeedStore.Study[tag].vr; + let tagSchema = getVRSchema(vr); + dicomSeriesSchema.add({ + [tag]: tagSchema + }); +} + +for (let tag in tagsNeedStore.Series) { + let vr = tagsNeedStore.Series[tag].vr; + let tagSchema = getVRSchema(vr); + dicomSeriesSchema.add({ + [tag]: tagSchema + }); +} + +dicomSeriesSchema.index({ + studyUID: 1 +}); +dicomSeriesSchema.index({ + seriesUID: 1 +}); +dicomSeriesSchema.index({ + "0020000D": 1 +}); +dicomSeriesSchema.index({ + "0020000E": 1 +}); + +let dicomSeriesModel = mongoose.model( + "dicomSeries", + dicomSeriesSchema, + "dicomSeries" +); + +module.exports = dicomSeriesModel; +module.exports.SeriesModel = dicomSeriesModel; diff --git a/models/mongodb/models/study.model.js b/models/mongodb/models/study.model.js new file mode 100644 index 00000000..2b0cc386 --- /dev/null +++ b/models/mongodb/models/study.model.js @@ -0,0 +1,107 @@ +const _ = require("lodash"); +const fsP = require("fs/promises"); +const path = require("path"); +const { raccoonConfig } = require("@root/config-class"); +const mongoose = require("mongoose"); +const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); +const { getVRSchema } = require("../schema/dicomJsonAttribute"); +const { BaseDicomSchemaDef, DicomSchemaOptionsFactory, StudyDocDicomJsonHandler } = require("../schema/dicom.schema"); +const { logger } = require("@root/utils/logs/log"); +const { IncludeFieldsFactory } = require("../service"); + +let dicomStudySchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("study", StudyDocDicomJsonHandler), + { + methods: { + incrementDeleteStatus: async function() { + await Promise.all([ + mongoose.model("dicomSeries").updateMany({ + studyUID: this.studyUID + }, { + $inc : { + deleteStatus: 1 + } + }), + mongoose.model("dicom").updateMany({ + studyUID: this.studyUID + }, { + $inc: { + deleteStatus: 1 + } + }) + ]); + this.deleteStatus += 1; + await this.save(); + }, + deleteDicomInstances: async function () { + + let studyPath = this.studyPath; + logger.warn("Permanently delete study folder: " + studyPath); + await fsP.rm(path.join(raccoonConfig.dicomWebConfig.storeRootPath, studyPath), { + force: true, + recursive: true + }); + } + }, + statics: { + findOneByDicomUID: async function({ studyUID }) { + return await mongoose.model("dicomStudy").findOne({ studyUID }).exec(); + }, + /** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ + getDicomJsonProjection: function (queryOptions) { + let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); + return includeFieldsFactory.getStudyLevelFields(); + }, + getPathGroupQuery: function (iParam) { + let { studyUID } = iParam; + return { + $match: { + studyUID: studyUID + } + }; + } + } + } +); + +let dicomStudySchema = new mongoose.Schema( + { + ...BaseDicomSchemaDef + }, + dicomStudySchemaOptions +); + +dicomStudySchema.add({ + studyPath: { + type: String, + default: void 0 + } +}); + +for (let tag in tagsNeedStore.Study) { + let vr = tagsNeedStore.Study[tag].vr; + let tagSchema = getVRSchema(vr); + dicomStudySchema.add({ + [tag]: tagSchema + }); +} + +dicomStudySchema.index({ + studyUID: 1 +}); +dicomStudySchema.index({ + "0020000D": 1 +}); + +let dicomStudyModel = mongoose.model( + "dicomStudy", + dicomStudySchema, + "dicomStudy" +); + +module.exports = dicomStudyModel; +module.exports.StudyModel = dicomStudyModel; diff --git a/models/mongodb/models/upsGlobalSubscription.js b/models/mongodb/models/upsGlobalSubscription.js index b81ec179..af796b04 100644 --- a/models/mongodb/models/upsGlobalSubscription.js +++ b/models/mongodb/models/upsGlobalSubscription.js @@ -2,7 +2,7 @@ const path = require("path"); const mongoose = require("mongoose"); const _ = require("lodash"); const { - SUBSCRIPTION_STATE + SUBSCRIPTION_STATE } = require("../../DICOM/ups"); let upsGlobalSubscriptionSchema = new mongoose.Schema( @@ -31,6 +31,34 @@ let upsGlobalSubscriptionSchema = new mongoose.Schema( versionKey: false, toObject: { getters: true + }, + statics: { + getCursor: async function (query, options) { + return await mongoose.model("upsGlobalSubscription").find(query, options).cursor(); + }, + createGlobalSubscription: async function (globalSubscription) { + return await mongoose.model("upsGlobalSubscription").create(globalSubscription); + }, + updateRepositoryInstance: async function (globalSubscription, query, deletionLock, subscribed) { + globalSubscription.isDeletionLock = deletionLock; + globalSubscription.subscribed = subscribed; + globalSubscription.queryKeys = query; + return await globalSubscription.save(); + }, + findOneByAeTitle: async function (aeTitle) { + return await mongoose.model("upsGlobalSubscription").findOne({ aeTitle: aeTitle }); + }, + /** + * + * @param {string} aeTitle + * @returns + */ + getCountByAeTitle: async function (aeTitle) { + return await mongoose.model("upsGlobalSubscription").countDocuments({ aeTitle: aeTitle }); + }, + deleteOneByAeTitle: async function (aeTitle) { + return await mongoose.model("upsGlobalSubscription").findOneAndDelete({ aeTitle: aeTitle }); + } } } ); @@ -43,3 +71,4 @@ let upsSubscriptionModel = mongoose.model( ); module.exports = upsSubscriptionModel; +module.exports.UpsGlobalSubscriptionModel = upsSubscriptionModel; diff --git a/models/mongodb/models/upsSubscription.js b/models/mongodb/models/upsSubscription.js index 2d271b8a..9149b688 100644 --- a/models/mongodb/models/upsSubscription.js +++ b/models/mongodb/models/upsSubscription.js @@ -31,6 +31,67 @@ let upsSubscriptionSchema = new mongoose.Schema( versionKey: false, toObject: { getters: true + }, + statics: { + findByWorkItem: async function (workItem) { + return await mongoose.model("upsSubscription").find({ workItems: workItem._id }).exec(); + }, + /** + * + * @param {string} aeTitle + * @returns repository item + */ + findOneByAeTitle: async function (aeTitle) { + return await mongoose.model("upsSubscription").findOne({ aeTitle }).exec(); + }, + createSubscriptionForWorkItem: async function (workItem, aeTitle, deletionLock, subscribed) { + let subscription = new mongoose.model("upsSubscription")({ + aeTitle: aeTitle, + workItems: [workItem._id], + subscribed: subscribed, + isDeletionLock: deletionLock + }); + return await subscription.save(); + }, + updateSubscription: async function (subscription, workItem, deletionLock, subscribed) { + return await mongoose.model("upsSubscription").findOneAndUpdate({ + _id: subscription._id + }, { + $set: { + isDeletionLock: deletionLock, + subscribed: subscribed + }, + $addToSet: { + workItems: workItem._id + } + }); + }, + /** + * + * @param {string} aeTitle + * @param {any} workItem repository item + */ + unsubscribe: async function (aeTitle, workItem) { + return await mongoose.model("upsSubscription").findOneAndUpdate({ + aeTitle: aeTitle, + workItems: workItem._id + }, { + $pull: { + workItems: workItem._id + } + }); + + }, + /** + * + * @param {string} aeTitle + */ + getCountByAeTitle: async function (aeTitle) { + return await mongoose.model("upsSubscription").countDocuments({ aeTitle: aeTitle }); + }, + deleteOneByAeTitle: async function (aeTitle) { + return await mongoose.model("upsSubscription").findOneAndDelete({ aeTitle: aeTitle }); + } } } ); @@ -43,3 +104,4 @@ let upsSubscriptionModel = mongoose.model( ); module.exports = upsSubscriptionModel; +module.exports.UpsSubscriptionModel = upsSubscriptionModel; diff --git a/models/mongodb/models/workItems.js b/models/mongodb/models/workItems.js deleted file mode 100644 index bea22c29..00000000 --- a/models/mongodb/models/workItems.js +++ /dev/null @@ -1,121 +0,0 @@ -const path = require("path"); -const mongoose = require("mongoose"); -const _ = require("lodash"); -const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); -const { getVRSchema } = require("../schema/dicomJsonAttribute"); -const { SUBSCRIPTION_STATE } = require("../../DICOM/ups"); - -let workItemSchema = new mongoose.Schema( - { - upsInstanceUID: { - type: String, - default: void 0, - index: true, - required: true - }, - patientID: { - type: String, - default: void 0, - index: true, - required: true - }, - transactionUID: { - type: String, - default: void 0, - index: true - }, - subscribed: { - type: Number, - default: SUBSCRIPTION_STATE.NOT_SUBSCRIBED - } - }, - { - strict: true, - versionKey: false, - toObject: { - getters: true - } - } -); - -for (let tag in tagsNeedStore.UPS) { - let vr = tagsNeedStore.UPS[tag].vr; - let tagSchema = getVRSchema(vr); - workItemSchema.add({ - [tag]: tagSchema - }); -} - -for (let tag in tagsNeedStore.Patient) { - let vr = tagsNeedStore.Patient[tag].vr; - let tagSchema = getVRSchema(vr); - workItemSchema.add({ - [tag]: tagSchema - }); -} - -/** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ -workItemSchema.statics.getDicomJson = async function (queryOptions) { - let workItemFields = getWorkItemFields(); - - let { - workItem - } = queryOptions.requestParams; - - let query = workItem ? { - upsInstanceUID: workItem - } : queryOptions.query; - - try { - let docs = await mongoose.model("workItems").find(query, workItemFields) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .setOptions({ - strictQuery: false - }) - .exec(); - - - let workItemDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - return obj; - }); - - return workItemDicomJson; - - } catch (e) { - throw e; - } -}; - -function getWorkItemFields() { - return { - upsInstanceUID: 0, - patientID: 0, - transactionUID: 0, - "00081195": 0, - subscribed: 0 - }; -} - -/** - * @typedef { mongoose.Model & { - * getDicomJson: function(import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions): Promise - * }} WorkItemsModel -*/ - -/** @type {WorkItemsModel} */ -let workItemModel = mongoose.model( - "workItems", - workItemSchema, - "workItems" -); - -/** @type { WorkItemsModel } */ -module.exports = workItemModel; diff --git a/models/mongodb/models/workitems.model.js b/models/mongodb/models/workitems.model.js new file mode 100644 index 00000000..30fbd68f --- /dev/null +++ b/models/mongodb/models/workitems.model.js @@ -0,0 +1,204 @@ +const path = require("path"); +const mongoose = require("mongoose"); +const _ = require("lodash"); +const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); +const { getVRSchema } = require("../schema/dicomJsonAttribute"); +const { SUBSCRIPTION_STATE } = require("../../DICOM/ups"); +const { BaseDicomJson } = require("@models/DICOM/dicom-json-model"); +const { PatientModel } = require("./patient.model"); +const { convertRequestQueryToMongoQuery } = require("../convertQuery"); + +let workItemSchema = new mongoose.Schema( + { + upsInstanceUID: { + type: String, + default: void 0, + index: true, + required: true + }, + patientID: { + type: String, + default: void 0, + index: true, + required: true + }, + transactionUID: { + type: String, + default: void 0, + index: true + }, + subscribed: { + type: Number, + default: SUBSCRIPTION_STATE.NOT_SUBSCRIBED + } + }, + { + strict: true, + versionKey: false, + toObject: { + getters: true + }, + statics: { + findNotSubscribedWorkItems: async function () { + return await mongoose.model("workItems").find({ + $or: [ + { + subscribed: SUBSCRIPTION_STATE.NOT_SUBSCRIBED + }, + { + subscribed: { + $exists: false + } + } + ] + + }) || []; + }, + /** + * + * @param {Object} workItem general dicom json + */ + createWorkItemAndPatient: async function (workItem) { + let patientID = _.get(workItem, "00100020.Value.0"); + workItem.patientID = patientID; + + await PatientModel.findOneOrCreatePatient(patientID, workItem); + + let workItemDoc = new mongoose.model("workItems")(workItem); + return await workItemDoc.save(); + }, + /** + * + * @param {string} upsInstanceUID + * @returns + */ + findOneByUpsInstanceUID: async function (upsInstanceUID) { + return await mongoose.model("workItems").findOne({ + upsInstanceUID + }); + }, + /** + * + * @param {Object} query the query structure example { "00100010.Value": "foo" } or { "00100010.Value.00100010.Value": "bar" } + * @param {string} upsInstanceUID + * @returns {number} count + */ + async getCountWithQueryAndUpsInstanceUID(query, upsInstanceUID) { + let { $match } = await convertRequestQueryToMongoQuery(query); + $match.$and.push({ + upsInstanceUID: upsInstanceUID + }); + return await mongoose.model("workItems").countDocuments({ + ...$match + }); + }, + /** + * + * @param {string} upsInstanceUID + * @param {import("@root/utils/typeDef/dicom").GeneralDicomJson} generalDicomJson + */ + updateOneByUpsInstanceUID: async function (upsInstanceUID, generalDicomJson) { + return await mongoose.model("workItems").findOneAndUpdate({ + upsInstanceUID + }, generalDicomJson, {new: true}).exec(); + } + }, + methods: { + toDicomJson: async function () { + return new BaseDicomJson(await this.toGeneralDicomJson()); + }, + toGeneralDicomJson: async function () { + let obj = this.toObject(); + + delete obj._id; + delete obj.id; + delete obj.upsInstanceUID; + delete obj.patientID; + delete obj.transactionUID; + delete obj.subscribed; + + return obj; + }, + subscribe: async function (subscription) { + this.subscribed = subscription; + return await this.save(); + } + } + } +); + +for (let tag in tagsNeedStore.UPS) { + let vr = tagsNeedStore.UPS[tag].vr; + let tagSchema = getVRSchema(vr); + workItemSchema.add({ + [tag]: tagSchema + }); +} + +for (let tag in tagsNeedStore.Patient) { + let vr = tagsNeedStore.Patient[tag].vr; + let tagSchema = getVRSchema(vr); + workItemSchema.add({ + [tag]: tagSchema + }); +} + +/** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ +workItemSchema.statics.getDicomJson = async function (queryOptions) { + let workItemFields = getWorkItemFields(); + + let { + workItem + } = queryOptions.requestParams; + + let query = workItem ? { + upsInstanceUID: workItem + } : queryOptions.query; + + try { + let docs = await mongoose.model("workItems").find(query, workItemFields) + .limit(queryOptions.limit) + .skip(queryOptions.skip) + .setOptions({ + strictQuery: false + }) + .exec(); + + + let workItemDicomJson = docs.map((v) => { + let obj = v.toObject(); + delete obj._id; + delete obj.id; + return obj; + }); + + return workItemDicomJson; + + } catch (e) { + throw e; + } +}; + +function getWorkItemFields() { + return { + upsInstanceUID: 0, + patientID: 0, + transactionUID: 0, + "00081195": 0, + subscribed: 0 + }; +} + +let workItemModel = mongoose.model( + "workItems", + workItemSchema, + "workItems" +); + +/** @type { WorkItemsModel } */ +module.exports = workItemModel; +module.exports.WorkItemModel = workItemModel; diff --git a/models/mongodb/schema/dicom.schema.js b/models/mongodb/schema/dicom.schema.js new file mode 100644 index 00000000..cfdf73cc --- /dev/null +++ b/models/mongodb/schema/dicom.schema.js @@ -0,0 +1,277 @@ +const fsP = require("fs/promises"); +const path = require("path"); +const mongoose = require("mongoose"); +const _ = require("lodash"); +const { raccoonConfig } = require("@root/config-class"); +const { logger } = require("@root/utils/logs/log"); +const { IncludeFieldsFactory, getStoreDicomFullPath, getStoreDicomFullPathGroup } = require("../service"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); + +let Common; +if (raccoonConfig.dicomDimseConfig.enableDimse) { + require("@models/DICOM/dcm4che/java-instance"); + Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; +} + +const BaseDicomSchemaDef = { + studyUID: { + type: String, + default: void 0, + index: true, + required: true + }, + deleteStatus: { + type: Number, + default: 0 + } +}; + +class DicomSchemaOptionsFactory { + constructor() {} + + /** + * + * @param {"patient" |"study" | "series" | "instance"} level + * @param {typeof DocDicomJsonHandler} docDicomJsonHandlerType + * @returns + */ + static get(level, docDicomJsonHandlerType) { + return { + strict: true, + versionKey: false, + toObject: { + getters: true + }, + methods: { + incrementDeleteStatus: async function () { + this.deleteStatus = this.deleteStatus + 1; + await this.save(); + }, + deleteDicomInstances: async function() { + throw new Error("Not Implemented"); + }, + getAttributes: async function () { + let doc = this.toObject(); + delete doc._id; + delete doc.id; + + let jsonStr = JSON.stringify(doc); + return await Common.getAttributesFromJsonString(jsonStr); + } + }, + statics: { + getDimseResultCursor: async function (query, keys) { + return mongoose.model(DicomModelNames[level]).find(query, keys).setOptions({ + strictQuery: false + }) + .cursor(); + }, + /** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ + getDicomJson: async function (queryOptions) { + let projection = mongoose.model(DicomModelNames[level]).getDicomJsonProjection(queryOptions); + + try { + let docs = await mongoose.model(DicomModelNames[level]).find({ + ...queryOptions.query, + deleteStatus: { + $eq: queryOptions.isRecycle ? 1 : 0 + } + }, projection) + .limit(queryOptions.limit) + .skip(queryOptions.skip) + .setOptions({ + strictQuery: false + }) + .exec(); + + let docDicomJsonHandler = new docDicomJsonHandlerType(docs, queryOptions); + let dicomJson = docDicomJsonHandler.get(); + + return dicomJson; + + } catch (e) { + throw e; + } + }, + /** + * + * @param {import("@root/utils/typeDef/dicom").DicomJsonQueryOptions} queryOptions + * @returns + */ + getDicomJsonProjection: function (queryOptions) { + throw new Error("Not Implemented"); + }, + /** + * + * @param {Object} iParam + * @param {string} iParam.studyUID + */ + getPathGroupOfInstances: async function (iParam) { + try { + let query = [ + { + ...mongoose.model(DicomModelNames[level]).getPathGroupQuery(iParam) + }, + { + ...mongoose.model(DicomModelNames[level]).getPathGroupQueryOptions() + } + ]; + let docs = await mongoose.model("dicom").aggregate(query).exec(); + let pathGroup = _.get(docs, "0.pathList", []); + + let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); + + return fullPathGroup; + + } catch (e) { + throw e; + } + }, + getPathGroupQueryOptions: function (level) { + return { + $group: { + _id: PathGroupIdField[level], + pathList: { + $addToSet: { + studyUID: "$studyUID", + seriesUID: "$seriesUID", + instanceUID: "$instanceUID", + instancePath: "$instancePath" + } + } + } + }; + }, + getPathGroupQuery: function (iParam) { + throw new Error("Not Implemented"); + } + }, + timestamps: true + }; + } +} + +const PathGroupIdField = Object.freeze({ + "patient": "$patientID", + "study": "$studyUID", + "series": "$seriesUID", + "instance": "$instanceUID" +}); + +const DicomModelNames = Object.freeze({ + "patient": "patient", + "study": "dicomStudy", + "series": "dicomSeries", + "instance": "dicom" +}); + +class DocDicomJsonHandler { + constructor(docs, queryOptions) { + this.docs = docs; + this.queryOptions = queryOptions; + } + + /** + * @private + * @param {any} obj + * @returns + */ + getPreprocessedDoc_(obj) { + let preProcessedDoc = obj.toObject(); + delete preProcessedDoc._id; + delete preProcessedDoc.id; + return preProcessedDoc; + } + + get() { + if (this.docs) { + return this.docs?.map((v) => { + let obj = this.getPreprocessedDoc_(v); + + this.setRetrieveUrl(obj); + this.setRetrieveAETitle(obj); + + return obj; + }); + } + return []; + } + + setRetrieveUrl(obj) { + _.set(obj, dictionary.keyword.RetrieveURL, { + ...dictionary.tagVR[dictionary.keyword.RetrieveURL], + Value: this.getRetrieveUrlValue(obj) + }); + } + + getRetrieveUrlValue(obj) { + throw new Error("Not Implemented"); + } + + setRetrieveAETitle(obj) { + _.set(obj, dictionary.keyword.RetrieveAETitle, { + ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], + Value: [raccoonConfig.aeTitle] + }); + } +} + +class PatientDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + setRetrieveUrl(obj) { + return; + } + + setRetrieveAETitle(obj) { + return; + } +} + +class StudyDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + getRetrieveUrlValue(obj) { + return [`${this.queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}`]; + } +} + +class SeriesDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + getRetrieveUrlValue(obj) { + return [ + `${this.queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}` + ]; + } +} + +class InstanceDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + getRetrieveUrlValue(obj) { + return [ + `${this.queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}/instances/${obj["00080018"]["Value"][0]}` + ]; + } +} + + +module.exports.BaseDicomSchemaDef = BaseDicomSchemaDef; +module.exports.DicomSchemaOptionsFactory = DicomSchemaOptionsFactory; +module.exports.PatientDocDicomJsonHandler = PatientDocDicomJsonHandler; +module.exports.StudyDocDicomJsonHandler = StudyDocDicomJsonHandler; +module.exports.SeriesDocDicomJsonHandler = SeriesDocDicomJsonHandler; +module.exports.InstanceDocDicomJsonHandler = InstanceDocDicomJsonHandler; \ No newline at end of file diff --git a/models/mongodb/service.js b/models/mongodb/service.js index fe7ab819..6e6a98ca 100644 --- a/models/mongodb/service.js +++ b/models/mongodb/service.js @@ -320,6 +320,19 @@ class IncludeFieldsFactory { }; } + getMwlLevelFields() { + if (this.all) { + return {}; + } + + let fields = {}; + for (let tag in tagsOfRequiredMatching.Mwl) { + fields[tag] = 1; + } + + return fields; + } + /** * @private */ diff --git a/package-lock.json b/package-lock.json index a1468b7e..884162d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,18 +10,18 @@ "license": "MIT", "dependencies": { "@jorgeferrero/stream-to-buffer": "^2.0.6", - "archiver": "^5.3.1", - "axios": "^0.26.1", + "7zip-min": "^1.4.4", + "axios": "^1.6.2", "bcrypt": "^5.0.1", "body-parser": "^1.20.0", "colorette": "^2.0.20", "commander": "^10.0.1", "compression": "^1.7.4", - "connect-mongo": "^4.6.0", + "connect-mongo": "^5.1.0", + "connect-session-sequelize": "^7.1.7", "cookie-parser": "^1.4.6", "cors": "^2.8.5", - "dicom-parser": "^1.8.13", - "dicom-to-json": "^1.2.1", + "dicomjson-to-fhir": "^1.0.1", "dotenv": "^16.0.3", "env-var": "^7.3.1", "express": "^4.18.2", @@ -29,6 +29,7 @@ "flat": "^5.0.2", "formidable": "^2.0.1", "iconv-lite": "^0.6.3", + "image-size": "^1.1.1", "imagemagick-cli": "^0.5.0", "java-bridge": "^2.3.0", "joi": "^17.6.0", @@ -44,13 +45,15 @@ "passport": "^0.6.0", "passport-local": "^1.0.0", "path-match": "^1.2.4", + "pg": "^8.11.1", + "pg-hstore": "^2.3.4", "regexparam": "^2.0.1", "request-compose": "^2.1.6", "request-multipart": "^1.0.0", "run-script-os": "^1.1.6", - "sharp": "^0.30.4", + "sequelize": "^6.32.1", "shorthash2": "^1.0.3", - "uuid": "^9.0.0", + "uuid": "^9.0.1", "ws": "^8.13.0" }, "devDependencies": { @@ -58,6 +61,7 @@ "eslint-config-prettier": "^8.5.0", "mocha": "^10.2.0", "mongodb-memory-server": "^8.12.2", + "sequelize-erd": "^1.3.1", "standard-version": "^9.5.0", "swagger-jsdoc": "^6.2.8" } @@ -831,6 +835,95 @@ "node": ">=6.9.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jorgeferrero/stream-to-buffer": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@jorgeferrero/stream-to-buffer/-/stream-to-buffer-2.0.6.tgz", @@ -842,6 +935,14 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "dev": true }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz", @@ -861,6 +962,14 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1411,6 +1520,14 @@ "node": ">=14.0.0" } }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -1423,6 +1540,11 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "node_modules/@types/node": { "version": "17.0.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", @@ -1434,20 +1556,38 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==" + }, "node_modules/@types/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, "node_modules/@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.3.tgz", + "integrity": "sha512-z1ELvMijRL1QmU7QuzDkeYXSF2+dXI0ITKoQsIoVKcNBOiK5RMmWy+pYYxJTHFt8vkpZe7UsvRErQwcxZkjoUw==", + "peer": true, "dependencies": { - "@types/node": "*", "@types/webidl-conversions": "*" } }, + "node_modules/7zip-bin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", + "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==" + }, + "node_modules/7zip-min": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/7zip-min/-/7zip-min-1.4.4.tgz", + "integrity": "sha512-mYB1WW5tcXfZxUN4+2joKk4+6j8jp+mpO2YiMU5z1gNNFbACxI2ADasffsdNPovZSwn/E662ZIH5gRkFPMufmA==", + "dependencies": { + "7zip-bin": "5.1.1" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1466,9 +1606,9 @@ } }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "peer": true, "bin": { @@ -1522,11 +1662,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", - "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==" - }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1576,70 +1711,6 @@ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, - "node_modules/archiver": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", - "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.3", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", @@ -1703,11 +1774,6 @@ "node": "*" } }, - "node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, "node_modules/async-mutex": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", @@ -1717,12 +1783,19 @@ "tslib": "^2.3.1" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/balanced-match": { @@ -1762,26 +1835,6 @@ "node": ">= 10.0.0" } }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1791,14 +1844,6 @@ "node": ">=8" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -1922,6 +1967,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, "engines": { "node": "*" } @@ -1932,25 +1978,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" - }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", "engines": { - "node": ">=0.2.0" + "node": ">=4" } }, "node_modules/bytes": { @@ -1989,14 +2022,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/camelcase-keys": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", @@ -2041,17 +2066,6 @@ "node": ">=4" } }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2179,367 +2193,112 @@ "node": ">=10" } }, - "node_modules/cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "engines": { - "node": ">=0.10.0" + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" } }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "number-is-nan": "^1.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/cliui/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "node_modules/cmake-js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.0.tgz", - "integrity": "sha512-1uqTOmFt6BIqKlrX+39/aewU/JVhyZWDqwAL+6psToUwxj3yWPJiwxiZFmV0XdcoWmqGs7peZTxTbJtAcH8hxw==", - "dependencies": { - "axios": "^0.21.1", - "debug": "^4", - "fs-extra": "^5.0.0", - "is-iojs": "^1.0.1", - "lodash": "^4", - "memory-stream": "0", - "npmlog": "^1.2.0", - "rc": "^1.2.7", - "semver": "^5.0.3", - "splitargs": "0", - "tar": "^4", - "unzipper": "^0.8.13", - "url-join": "0", - "which": "^1.0.9", - "yargs": "^3.6.0" - }, - "bin": { - "cmake-js": "bin/cmake-js" + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": ">= 10.0.0" + "node": ">= 0.6" } }, - "node_modules/cmake-js/node_modules/are-we-there-yet": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", - "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.0 || ^1.1.13" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/cmake-js/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" } }, - "node_modules/cmake-js/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/cmake-js/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "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==", "dependencies": { - "minipass": "^2.6.0" - } - }, - "node_modules/cmake-js/node_modules/gauge": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", - "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", - "dependencies": { - "ansi": "^0.3.0", - "has-unicode": "^2.0.0", - "lodash.pad": "^4.1.0", - "lodash.padend": "^4.1.0", - "lodash.padstart": "^4.1.0" - } - }, - "node_modules/cmake-js/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "node_modules/cmake-js/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "node_modules/cmake-js/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/cmake-js/node_modules/npmlog": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", - "integrity": "sha1-KOe+YZYJtT960d0wChDWTXFiaLY=", - "dependencies": { - "ansi": "~0.3.0", - "are-we-there-yet": "~1.0.0", - "gauge": "~1.2.0" - } - }, - "node_modules/cmake-js/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/cmake-js/node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/cmake-js/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/cmake-js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/cmake-js/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/cmake-js/node_modules/tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "dependencies": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "engines": { - "node": ">=4.5" - } - }, - "node_modules/cmake-js/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/compress-commons": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", - "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "engines": { - "node": ">= 0.8" - } - }, - "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==", - "dependencies": { - "ms": "2.0.0" + "ms": "2.0.0" } }, "node_modules/compression/node_modules/ms": { @@ -2573,18 +2332,33 @@ } }, "node_modules/connect-mongo": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-4.6.0.tgz", - "integrity": "sha512-8new4Z7NLP3CGP65Aw6ls3xDBeKVvHRSh39CXuDZTQsvpeeU9oNMzfFgvqmHqZ6gWpxIl663RyoVEmCAGf1yOg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.1.0.tgz", + "integrity": "sha512-xT0vxQLqyqoUTxPLzlP9a/u+vir0zNkhiy9uAdHjSCcUUf7TS5b55Icw8lVyYFxfemP3Mf9gdwUOgeF3cxCAhw==", "dependencies": { "debug": "^4.3.1", "kruptein": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=12.9.0" + }, + "peerDependencies": { + "express-session": "^1.17.1", + "mongodb": ">= 5.1.0 < 7" + } + }, + "node_modules/connect-session-sequelize": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-7.1.7.tgz", + "integrity": "sha512-Wqq7rg0w+9bOVs6jC0nhZnssXJ3+iKNlDVWn2JfBuBPoY7oYaxzxfBKeUYrX6dHt3OWEWbZV6LJvapwi76iBQQ==", + "dependencies": { + "debug": "^4.1.1" + }, + "engines": { + "node": ">= 10" }, "peerDependencies": { - "mongodb": "^4.1.0" + "sequelize": ">= 6.1.0" } }, "node_modules/console-control-strings": { @@ -2900,7 +2674,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cors": { "version": "2.8.5", @@ -2914,29 +2689,6 @@ "node": ">= 0.10" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", - "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/cron-parser": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", @@ -3001,6 +2753,11 @@ "node": "*" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3021,6 +2778,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -3050,17 +2808,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -3073,14 +2820,6 @@ "node": ">=6" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3088,6 +2827,14 @@ "dev": true, "peer": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -3120,9 +2867,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { "node": ">=8" } @@ -3145,32 +2892,15 @@ "wrappy": "1" } }, - "node_modules/dicom-parser": { - "version": "1.8.13", - "resolved": "https://registry.npmjs.org/dicom-parser/-/dicom-parser-1.8.13.tgz", - "integrity": "sha512-8M53FPHS4zM3zvu5fdIWdatqrjpiG2+2M6RJ0IxwqLF4gvCYRsqUIusxYaOiNU0sWaptUpnXeZiXunP0LOIcQw==" - }, - "node_modules/dicom-to-json": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/dicom-to-json/-/dicom-to-json-1.2.1.tgz", - "integrity": "sha512-1AbKqqQUrJbbRpGy6BKw7wldPyL6fHZsnaRVtBB3VPhnJ26cTPZ+rGFBGiDmKPVlgAklCftmclXC3boMAnoBRA==", - "hasInstallScript": true, + "node_modules/dicomjson-to-fhir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dicomjson-to-fhir/-/dicomjson-to-fhir-1.0.1.tgz", + "integrity": "sha512-V3YlmOBp30RDk3t04GwHa/ZlbgMUnNrV06AEtBgZyENWYhrq2awLaOWKdo6s3/fT4f1Pha/qK/9VluRogUp1yw==", "dependencies": { - "bindings": "^1.5.0", - "cmake-js": "^6.3.0", - "node-addon-api": "^4.3.0", - "prebuild-install": "^6.1.4", - "run-script-os": "^1.1.6" - }, - "engines": { - "node": ">=10" + "dayjs": "^1.11.10", + "uid": "^2.0.2" } }, - "node_modules/dicom-to-json/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" - }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -3271,42 +3001,17 @@ "node": ">=4" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/ee-first": { + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" @@ -3328,6 +3033,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "dependencies": { "once": "^1.4.0" } @@ -3597,14 +3303,6 @@ "node": ">= 0.6" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -3787,11 +3485,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3892,9 +3585,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -3926,9 +3619,9 @@ } }, "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", - "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "engines": { "node": ">=14" }, @@ -3936,6 +3629,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/formidable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", @@ -3969,17 +3675,8 @@ "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true }, "node_modules/fs-minipass": { "version": "2.1.0", @@ -4011,42 +3708,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -4088,9 +3749,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" @@ -4300,11 +3961,6 @@ "ini": "^1.3.2" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -4511,15 +4167,29 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "peer": true, "engines": { "node": ">= 4" } }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, "node_modules/imagemagick-cli": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/imagemagick-cli/-/imagemagick-cli-0.5.0.tgz", @@ -4564,6 +4234,14 @@ "node": ">=8" } }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4581,15 +4259,8 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true }, "node_modules/ip": { "version": "2.0.0", @@ -4604,11 +4275,6 @@ "node": ">= 0.10" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4662,11 +4328,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-iojs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", - "integrity": "sha1-TBEDO11dlNbqs3dd7cm+fQCDJfE=" - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4709,7 +4370,8 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", @@ -4722,11 +4384,11 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "node_modules/jackspeak": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.0.3.tgz", - "integrity": "sha512-0Jud3OMUdMbrlr3PyUMKESq51LXVAB+a239Ywdvd+Kgxj3MaBRml/nVRxf8tQFyfthMjuRkxkv7Vg58pmIMfuQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.0.tgz", + "integrity": "sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==", "dependencies": { - "cliui": "^7.0.4" + "@isaacs/cliui": "^8.0.2" }, "engines": { "node": ">=14" @@ -4738,55 +4400,29 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jackspeak/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/jackspeak/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/java-bridge": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge/-/java-bridge-2.3.0.tgz", - "integrity": "sha512-qQqooQMY+dyWKbQp67pfc1WZncVNSZFYKf7RQOY2tWYQNBrA4xGsQ8G5VbffXcuW61/Ezja/XrbiHpd3a22Oaw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge/-/java-bridge-2.4.0.tgz", + "integrity": "sha512-KxJCs1DnmxPVol8N6+N7y89u9wfLG5oSkm0xoBBJ7CLEhuuTc/1hiFITjrEEub66AwNZDr2byYB5N2lbUQhrkg==", "dependencies": { - "glob": "^10.0.0" + "glob": "^10.3.3" }, "engines": { "node": ">= 15" }, "optionalDependencies": { - "java-bridge-darwin-arm64": "2.3.0", - "java-bridge-darwin-x64": "2.3.0", - "java-bridge-linux-arm64-gnu": "2.3.0", - "java-bridge-linux-x64-gnu": "2.3.0", - "java-bridge-win32-ia32-msvc": "2.3.0", - "java-bridge-win32-x64-msvc": "2.3.0" + "java-bridge-darwin-arm64": "2.4.0", + "java-bridge-darwin-x64": "2.4.0", + "java-bridge-linux-arm64-gnu": "2.4.0", + "java-bridge-linux-x64-gnu": "2.4.0", + "java-bridge-win32-ia32-msvc": "2.4.0", + "java-bridge-win32-x64-msvc": "2.4.0" } }, "node_modules/java-bridge-darwin-arm64": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-darwin-arm64/-/java-bridge-darwin-arm64-2.3.0.tgz", - "integrity": "sha512-jIEly/4h0zZtRv1KA95kg8H4RVp7KUa+rn6bpLupfB/9uVtcM8uf1bFurvZRkGM41JYbM+y7KcotoKTFUgUKXA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-darwin-arm64/-/java-bridge-darwin-arm64-2.4.0.tgz", + "integrity": "sha512-dIFMV6w+lXnOU6xaeOmKsUOloIAzm577mkmi7Cim3Lue2nLZUFhDbwLiHTcnUTbq5+d7Qb8JbkdNTE2AjaPLmg==", "cpu": [ "arm64" ], @@ -4799,9 +4435,9 @@ } }, "node_modules/java-bridge-darwin-x64": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-darwin-x64/-/java-bridge-darwin-x64-2.3.0.tgz", - "integrity": "sha512-ukhU321HSsofgEfnLm9QUbG09U3shwxulRVqIpKIalB+GB+HRF1Op8FzyRJOkA0Jzc0O36uq7sVQ7HBGEtF76A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-darwin-x64/-/java-bridge-darwin-x64-2.4.0.tgz", + "integrity": "sha512-2Fj9DUynyP6Wt6ceAmeCgkSaUkW20bOtRN/0JTqQe2wwFVGafFxzUjWUYygHlGG26Gtik3kJzic+c7Sxr1lFUQ==", "cpu": [ "x64" ], @@ -4814,9 +4450,9 @@ } }, "node_modules/java-bridge-linux-arm64-gnu": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-linux-arm64-gnu/-/java-bridge-linux-arm64-gnu-2.3.0.tgz", - "integrity": "sha512-ITV+7aFle1ucNhoREOGpem/ez8sCjePKsd+GC/JQ4wBLqNuPhfbvu7qCSHJiB9c/FeRUiLTh9NaBfHyuip92ng==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-linux-arm64-gnu/-/java-bridge-linux-arm64-gnu-2.4.0.tgz", + "integrity": "sha512-5IyZ5t6JDxEgjsOazhS+ZyBFjpSKN2agNFuCuW+R8wQmZMj459kXhsIZEHujtp7MobmMXc1dTcwaV8n3mqaR1w==", "cpu": [ "arm64" ], @@ -4829,9 +4465,9 @@ } }, "node_modules/java-bridge-linux-x64-gnu": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-linux-x64-gnu/-/java-bridge-linux-x64-gnu-2.3.0.tgz", - "integrity": "sha512-Nzk97+9sOR4gMJJPYZd/o9OgkkhuWW5BaxSOmBxAhyZk4O6hha5T2w3QtvrqcfkbpyOY8yeaZdGq3k0n86ALnA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-linux-x64-gnu/-/java-bridge-linux-x64-gnu-2.4.0.tgz", + "integrity": "sha512-9V6kRmEbX1FFGqzRWr+NK9jxf9otiw9wCuLBoLBj/iErAjQpia6l061Z7vydt5c4Yu/lk107oBUo/Hsbtc/syQ==", "cpu": [ "x64" ], @@ -4844,9 +4480,9 @@ } }, "node_modules/java-bridge-win32-ia32-msvc": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-win32-ia32-msvc/-/java-bridge-win32-ia32-msvc-2.3.0.tgz", - "integrity": "sha512-MG4R1OSiCq20a1a/w4Hf5NtnNIulHhc0DnqomS/s09nH+xcVcDV9eQQ12pa7FoCZtK7nkVRwV/xhLQ8wtcK6zg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-win32-ia32-msvc/-/java-bridge-win32-ia32-msvc-2.4.0.tgz", + "integrity": "sha512-D+Im+AAiPgbT0BVWXbuSVQcbek4+VK6SHUVCXF4Gi02Yew/SJ+aNgIIkjP0TMVeYrxG1AlwcYQ80ahuzOm1acA==", "cpu": [ "ia32" ], @@ -4859,9 +4495,9 @@ } }, "node_modules/java-bridge-win32-x64-msvc": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-win32-x64-msvc/-/java-bridge-win32-x64-msvc-2.3.0.tgz", - "integrity": "sha512-dDDD/+plvden+VHA2Zr5DebGDFnO15wpp+Udhn/XZjUBmfDto03BI814IsfTziDv0h/GDZIXVxvAnjtuQyBkGw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-win32-x64-msvc/-/java-bridge-win32-x64-msvc-2.4.0.tgz", + "integrity": "sha512-gK43rdXS6w7pNkz7DDHFnpj4A9v5yu5HPdFU4PankuQzlvLC5r4/S1hBXR4jvI5+md9c8LnvLaxLQwQ+OUfE8A==", "cpu": [ "x64" ], @@ -4882,16 +4518,15 @@ } }, "node_modules/java-bridge/node_modules/glob": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.1.tgz", - "integrity": "sha512-ngom3wq2UhjdbmRE/krgkD8BQyi1KZ5l+D2dVm4+Yj+jJIBp74/ZGunL6gNGc/CYuQmvUBiavWEXIotRiv5R6A==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", "dependencies": { "foreground-child": "^3.1.0", - "fs.realpath": "^1.0.0", "jackspeak": "^2.0.3", - "minimatch": "^9.0.0", - "minipass": "^5.0.0", - "path-scurry": "^1.7.0" + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/cjs/src/bin.js" @@ -4904,9 +4539,9 @@ } }, "node_modules/java-bridge/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4918,11 +4553,11 @@ } }, "node_modules/java-bridge/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/joi": { @@ -5056,55 +4691,6 @@ "node": ">8" } }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5125,11 +4711,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" - }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -5171,21 +4752,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -5204,11 +4770,6 @@ "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5222,26 +4783,6 @@ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, - "node_modules/lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=" - }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=" - }, - "node_modules/lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" - }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, "node_modules/log4js": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", @@ -5347,37 +4888,7 @@ "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "node_modules/memory-stream": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", - "integrity": "sha1-6+jdHDuLw4wOeUHp3dWuvmtN6D8=", - "dependencies": { - "readable-stream": "~1.0.26-2" - } - }, - "node_modules/memory-stream/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "node_modules/memory-stream/node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/memory-stream/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, "node_modules/meow": { "version": "8.1.2", @@ -5554,17 +5065,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -5593,7 +5093,8 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "node_modules/minimist-options": { "version": "4.1.0", @@ -5652,11 +5153,6 @@ "node": ">=10" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -5966,60 +5462,93 @@ } }, "node_modules/mongodb": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz", - "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "peer": true, "dependencies": { - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.5.4", - "socks": "^2.7.1" + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" }, "engines": { - "node": ">=12.9.0" + "node": ">=16.20.1" }, - "optionalDependencies": { - "@aws-sdk/credential-providers": "^3.186.0", - "saslprep": "^1.0.3" + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } } }, "node_modules/mongodb-connection-string-url": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.4.tgz", - "integrity": "sha512-SeAxuWs0ez3iI3vvmLk/j2y+zHwigTDKQhtdxTgt5ZCOQQS5+HW4g45/Xw5vzzbn7oQXCNQ24Z40AkJsizEy7w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "peer": true, "dependencies": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" } }, "node_modules/mongodb-connection-string-url/node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "peer": true, "dependencies": { - "punycode": "^2.1.1" + "punycode": "^2.3.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "peer": true, "engines": { "node": ">=12" } }, "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "peer": true, "dependencies": { - "tr46": "^3.0.0", + "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=16" } }, "node_modules/mongodb-memory-server": { @@ -6061,6 +5590,16 @@ "node": ">=12.22.0" } }, + "node_modules/mongodb-memory-server-core/node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "node_modules/mongodb-memory-server-core/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -6073,14 +5612,85 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", + "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", + "dev": true, + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dev": true, + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb/node_modules/bson": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", + "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", + "peer": true, + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/mongoose": { - "version": "6.11.6", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.11.6.tgz", - "integrity": "sha512-CuVbeJrEbnxkPUNNFvXJhjVyqa5Ip7lkz6EJX6g7Lb3aFMTJ+LHOlUrncxzC3r20dqasaVIiwcA6Y5qC8PWQ7w==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.12.3.tgz", + "integrity": "sha512-MNJymaaXali7w7rHBxVUoQ3HzHHMk/7I/+yeeoSa4rUzdjZwIWQznBNvVgc0A8ghuJwsuIkb5LyLV6gSjGjWyQ==", "dependencies": { "bson": "^4.7.2", "kareem": "2.5.1", - "mongodb": "4.16.0", + "mongodb": "4.17.1", "mpath": "0.9.0", "mquery": "4.0.3", "ms": "2.1.3", @@ -6094,11 +5704,77 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.1.tgz", + "integrity": "sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==", + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" + } + }, + "node_modules/mongoose/node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, "node_modules/mongoose/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/mongoose/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", @@ -6158,11 +5834,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6196,22 +5867,6 @@ "node": ">=12.22.0" } }, - "node_modules/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dependencies": { - "semver": "^5.4.1" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -6282,6 +5937,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6297,14 +5953,6 @@ "set-blocking": "^2.0.0" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6373,17 +6021,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dependencies": { - "lcid": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -6420,6 +6057,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6569,12 +6211,12 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", - "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dependencies": { - "lru-cache": "^9.0.0", - "minipass": "^5.0.0" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6584,19 +6226,19 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.0.tgz", - "integrity": "sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "engines": { "node": "14 || >=16.14" } }, "node_modules/path-scurry/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/path-to-regexp": { @@ -6645,181 +6287,171 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "node_modules/pg": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.1.tgz", + "integrity": "sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.1", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, "engines": { - "node": ">=8.6" + "node": ">= 8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + }, + "node_modules/pg-hstore": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.4.tgz", + "integrity": "sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA==", + "dependencies": { + "underscore": "^1.13.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.x" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", "dependencies": { - "find-up": "^4.0.0" + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/pgpass/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "engines": { - "node": ">=6" + "node": ">= 10.x" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/prebuild-install/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/prebuild-install/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "node_modules/prebuild-install/node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/prebuild-install/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" + "find-up": "^4.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/prebuild-install/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" } }, - "node_modules/prebuild-install/node_modules/is-fullwidth-code-point": { + "node_modules/postgres-bytea": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", "engines": { "node": ">=0.10.0" } }, - "node_modules/prebuild-install/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/prebuild-install/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/prebuild-install/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/prebuild-install/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/prebuild-install/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", "engines": { "node": ">=0.10.0" } }, - "node_modules/prebuild-install/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "dependencies": { - "ansi-regex": "^2.0.0" + "xtend": "^4.0.0" }, "engines": { "node": ">=0.10.0" @@ -6838,7 +6470,8 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -6852,19 +6485,15 @@ "node": ">= 0.10" } }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "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==" }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -6893,6 +6522,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -6952,20 +6589,6 @@ "node": ">=0.10.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -7100,14 +6723,6 @@ "node": ">= 6" } }, - "node_modules/readdir-glob": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz", - "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7231,6 +6846,11 @@ "node": ">=4" } }, + "node_modules/retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, "node_modules/rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", @@ -7283,18 +6903,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "optional": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -7350,6 +6958,102 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/sequelize": { + "version": "6.32.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.32.1.tgz", + "integrity": "sha512-3Iv0jruv57Y0YvcxQW7BE56O7DC1BojcfIrqh6my+IQwde+9u/YnuYHzK+8kmZLhLvaziRT1eWu38nh9yVwn/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.4", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.0", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.1", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-erd": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/sequelize-erd/-/sequelize-erd-1.3.1.tgz", + "integrity": "sha512-w5/gNkj0WTp80KMvMxrrTp3HyIn1B8F5XmTXP6PXQNoWi1HKazXe9IvlbmGVP2yxx/YTtX+QWatcwkDk8HLK9Q==", + "dev": true, + "dependencies": { + "commander": "^2.9.0", + "lodash": "^4.17.15" + }, + "bin": { + "sequelize-erd": "bin/generate" + } + }, + "node_modules/sequelize-erd/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -7378,128 +7082,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "node_modules/sharp": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz", - "integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==", - "hasInstallScript": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=12.13.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sharp/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sharp/node_modules/node-abi": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", - "integrity": "sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp/node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node_modules/sharp/node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp/node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7547,43 +7134,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -7623,8 +7173,7 @@ "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", - "optional": true, + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "dependencies": { "memory-pager": "^1.0.2" } @@ -7682,11 +7231,6 @@ "readable-stream": "^3.0.0" } }, - "node_modules/splitargs": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", - "integrity": "sha1-/p965lc3GzOxDLgNoUPPgknPazs=" - }, "node_modules/standard-version": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", @@ -7886,6 +7430,20 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stringify-package": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", @@ -7893,7 +7451,19 @@ "deprecated": "This module is not used anymore, and has been replaced by @npmcli/package-json", "dev": true }, - "node_modules/strip-ansi": { + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -7925,14 +7495,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", @@ -8041,26 +7603,11 @@ "node": ">= 10" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -8076,6 +7623,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -8133,19 +7681,16 @@ "node": ">=0.6" } }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "engines": { - "node": "*" - } - }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -8161,17 +7706,6 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "devOptional": true }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8238,6 +7772,17 @@ "node": ">=0.8.0" } }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -8249,6 +7794,11 @@ "node": ">= 0.8" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -8265,51 +7815,6 @@ "node": ">= 0.8" } }, - "node_modules/unzipper": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", - "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "~1.0.10", - "listenercount": "~1.0.1", - "readable-stream": "~2.1.5", - "setimmediate": "~1.0.4" - } - }, - "node_modules/unzipper/node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" - }, - "node_modules/unzipper/node_modules/process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", - "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", - "dependencies": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/unzipper/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8320,11 +7825,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", - "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=" - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8339,9 +7839,13 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -8367,7 +7871,6 @@ "version": "13.9.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "dev": true, "engines": { "node": ">= 0.10" } @@ -8394,17 +7897,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -8413,15 +7905,12 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", - "bin": { - "window-size": "cli.js" - }, - "engines": { - "node": ">= 0.10.0" + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" } }, "node_modules/word-wrap": { @@ -8446,59 +7935,21 @@ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" + "node": ">=10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -8530,16 +7981,10 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } }, - "node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -8554,20 +7999,6 @@ "node": ">= 6" } }, - "node_modules/yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "dependencies": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - }, "node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", @@ -8616,49 +8047,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -8710,19 +8098,6 @@ "engines": { "node": "^12.20.0 || >=14" } - }, - "node_modules/zip-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", - "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", - "dependencies": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } } }, "dependencies": { @@ -9405,6 +8780,64 @@ "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@jorgeferrero/stream-to-buffer": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@jorgeferrero/stream-to-buffer/-/stream-to-buffer-2.0.6.tgz", @@ -9416,6 +8849,11 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "dev": true }, + "@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==" + }, "@mapbox/node-pre-gyp": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz", @@ -9432,6 +8870,14 @@ "tar": "^6.1.11" } }, + "@mongodb-js/saslprep": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -9879,6 +9325,14 @@ "tslib": "^2.5.0" } }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "requires": { + "@types/ms": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -9891,6 +9345,11 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "@types/node": { "version": "17.0.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", @@ -9902,20 +9361,38 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, + "@types/validator": { + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==" + }, "@types/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, "@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.3.tgz", + "integrity": "sha512-z1ELvMijRL1QmU7QuzDkeYXSF2+dXI0ITKoQsIoVKcNBOiK5RMmWy+pYYxJTHFt8vkpZe7UsvRErQwcxZkjoUw==", + "peer": true, "requires": { - "@types/node": "*", "@types/webidl-conversions": "*" } }, + "7zip-bin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", + "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==" + }, + "7zip-min": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/7zip-min/-/7zip-min-1.4.4.tgz", + "integrity": "sha512-mYB1WW5tcXfZxUN4+2joKk4+6j8jp+mpO2YiMU5z1gNNFbACxI2ADasffsdNPovZSwn/E662ZIH5gRkFPMufmA==", + "requires": { + "7zip-bin": "5.1.1" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -9931,9 +9408,9 @@ } }, "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "peer": true }, @@ -9972,105 +9449,40 @@ "uri-js": "^4.2.2" } }, - "ansi": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", - "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==" - }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "archiver": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", - "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", - "requires": { - "archiver-utils": "^2.1.0", - "async": "^3.2.3", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - } - }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, "are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", @@ -10125,11 +9537,6 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, "async-mutex": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", @@ -10139,12 +9546,19 @@ "tslib": "^2.3.1" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "requires": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "balanced-match": { @@ -10166,34 +9580,12 @@ "node-addon-api": "^3.1.0" } }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" - }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - } - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -10291,7 +9683,8 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true }, "buffer-from": { "version": "1.1.2", @@ -10299,20 +9692,10 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==" - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==" + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" }, "bytes": { "version": "3.1.2", @@ -10341,11 +9724,6 @@ "dev": true, "peer": true }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==" - }, "camelcase-keys": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", @@ -10380,14 +9758,6 @@ "type-detect": "^4.0.5" } }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "requires": { - "traverse": ">=0.3.0 <0.4" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -10484,224 +9854,6 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "cmake-js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.0.tgz", - "integrity": "sha512-1uqTOmFt6BIqKlrX+39/aewU/JVhyZWDqwAL+6psToUwxj3yWPJiwxiZFmV0XdcoWmqGs7peZTxTbJtAcH8hxw==", - "requires": { - "axios": "^0.21.1", - "debug": "^4", - "fs-extra": "^5.0.0", - "is-iojs": "^1.0.1", - "lodash": "^4", - "memory-stream": "0", - "npmlog": "^1.2.0", - "rc": "^1.2.7", - "semver": "^5.0.3", - "splitargs": "0", - "tar": "^4", - "unzipper": "^0.8.13", - "url-join": "0", - "which": "^1.0.9", - "yargs": "^3.6.0" - }, - "dependencies": { - "are-we-there-yet": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", - "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.0 || ^1.1.13" - } - }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "gauge": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", - "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", - "requires": { - "ansi": "^0.3.0", - "has-unicode": "^2.0.0", - "lodash.pad": "^4.1.0", - "lodash.padend": "^4.1.0", - "lodash.padstart": "^4.1.0" - } - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "npmlog": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", - "integrity": "sha1-KOe+YZYJtT960d0wChDWTXFiaLY=", - "requires": { - "ansi": "~0.3.0", - "are-we-there-yet": "~1.0.0", - "gauge": "~1.2.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "requires": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -10715,15 +9867,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -10734,6 +9877,14 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -10755,17 +9906,6 @@ "dot-prop": "^5.1.0" } }, - "compress-commons": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", - "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", - "requires": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - } - }, "compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -10831,14 +9971,22 @@ } }, "connect-mongo": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-4.6.0.tgz", - "integrity": "sha512-8new4Z7NLP3CGP65Aw6ls3xDBeKVvHRSh39CXuDZTQsvpeeU9oNMzfFgvqmHqZ6gWpxIl663RyoVEmCAGf1yOg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.1.0.tgz", + "integrity": "sha512-xT0vxQLqyqoUTxPLzlP9a/u+vir0zNkhiy9uAdHjSCcUUf7TS5b55Icw8lVyYFxfemP3Mf9gdwUOgeF3cxCAhw==", "requires": { "debug": "^4.3.1", "kruptein": "^3.0.0" } }, + "connect-session-sequelize": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-7.1.7.tgz", + "integrity": "sha512-Wqq7rg0w+9bOVs6jC0nhZnssXJ3+iKNlDVWn2JfBuBPoY7oYaxzxfBKeUYrX6dHt3OWEWbZV6LJvapwi76iBQQ==", + "requires": { + "debug": "^4.1.1" + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -11082,7 +10230,8 @@ "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "cors": { "version": "2.8.5", @@ -11093,20 +10242,6 @@ "vary": "^1" } }, - "crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" - }, - "crc32-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", - "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", - "requires": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - } - }, "cron-parser": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", @@ -11152,6 +10287,11 @@ "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true }, + "dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -11163,7 +10303,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decamelize-keys": { "version": "1.1.1", @@ -11183,14 +10324,6 @@ } } }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "requires": { - "mimic-response": "^2.0.0" - } - }, "deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -11200,11 +10333,6 @@ "type-detect": "^4.0.0" } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -11212,6 +10340,11 @@ "dev": true, "peer": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -11234,9 +10367,9 @@ "dev": true }, "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" }, "detect-newline": { "version": "3.1.0", @@ -11253,28 +10386,13 @@ "wrappy": "1" } }, - "dicom-parser": { - "version": "1.8.13", - "resolved": "https://registry.npmjs.org/dicom-parser/-/dicom-parser-1.8.13.tgz", - "integrity": "sha512-8M53FPHS4zM3zvu5fdIWdatqrjpiG2+2M6RJ0IxwqLF4gvCYRsqUIusxYaOiNU0sWaptUpnXeZiXunP0LOIcQw==" - }, - "dicom-to-json": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/dicom-to-json/-/dicom-to-json-1.2.1.tgz", - "integrity": "sha512-1AbKqqQUrJbbRpGy6BKw7wldPyL6fHZsnaRVtBB3VPhnJ26cTPZ+rGFBGiDmKPVlgAklCftmclXC3boMAnoBRA==", + "dicomjson-to-fhir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dicomjson-to-fhir/-/dicomjson-to-fhir-1.0.1.tgz", + "integrity": "sha512-V3YlmOBp30RDk3t04GwHa/ZlbgMUnNrV06AEtBgZyENWYhrq2awLaOWKdo6s3/fT4f1Pha/qK/9VluRogUp1yw==", "requires": { - "bindings": "^1.5.0", - "cmake-js": "^6.3.0", - "node-addon-api": "^4.3.0", - "prebuild-install": "^6.1.4", - "run-script-os": "^1.1.6" - }, - "dependencies": { - "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" - } + "dayjs": "^1.11.10", + "uid": "^2.0.2" } }, "diff": { @@ -11352,42 +10470,15 @@ } } }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "requires": { - "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } + "dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "ee-first": { "version": "1.1.1", @@ -11408,6 +10499,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "requires": { "once": "^1.4.0" } @@ -11609,11 +10701,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" - }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -11768,11 +10855,6 @@ "flat-cache": "^3.0.4" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -11854,9 +10936,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" }, "foreground-child": { "version": "3.1.1", @@ -11868,12 +10950,22 @@ }, "dependencies": { "signal-exit": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", - "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" } } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "formidable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", @@ -11898,17 +10990,8 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true }, "fs-minipass": { "version": "2.1.0", @@ -11930,35 +11013,6 @@ "dev": true, "optional": true }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -11994,9 +11048,9 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { @@ -12162,11 +11216,6 @@ "ini": "^1.3.2" } }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" - }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -12303,12 +11352,20 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "peer": true }, + "image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "requires": { + "queue": "6.0.2" + } + }, "imagemagick-cli": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/imagemagick-cli/-/imagemagick-cli-0.5.0.tgz", @@ -12341,6 +11398,11 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -12358,12 +11420,8 @@ "ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true }, "ip": { "version": "2.0.0", @@ -12375,11 +11433,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -12418,11 +11471,6 @@ "is-extglob": "^2.1.1" } }, - "is-iojs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", - "integrity": "sha1-TBEDO11dlNbqs3dd7cm+fQCDJfE=" - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -12453,7 +11501,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -12466,48 +11515,26 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "jackspeak": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.0.3.tgz", - "integrity": "sha512-0Jud3OMUdMbrlr3PyUMKESq51LXVAB+a239Ywdvd+Kgxj3MaBRml/nVRxf8tQFyfthMjuRkxkv7Vg58pmIMfuQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.0.tgz", + "integrity": "sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==", "requires": { - "@pkgjs/parseargs": "^0.11.0", - "cliui": "^7.0.4" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" } }, "java-bridge": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge/-/java-bridge-2.3.0.tgz", - "integrity": "sha512-qQqooQMY+dyWKbQp67pfc1WZncVNSZFYKf7RQOY2tWYQNBrA4xGsQ8G5VbffXcuW61/Ezja/XrbiHpd3a22Oaw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge/-/java-bridge-2.4.0.tgz", + "integrity": "sha512-KxJCs1DnmxPVol8N6+N7y89u9wfLG5oSkm0xoBBJ7CLEhuuTc/1hiFITjrEEub66AwNZDr2byYB5N2lbUQhrkg==", "requires": { - "glob": "^10.0.0", - "java-bridge-darwin-arm64": "2.3.0", - "java-bridge-darwin-x64": "2.3.0", - "java-bridge-linux-arm64-gnu": "2.3.0", - "java-bridge-linux-x64-gnu": "2.3.0", - "java-bridge-win32-ia32-msvc": "2.3.0", - "java-bridge-win32-x64-msvc": "2.3.0" + "glob": "^10.3.3", + "java-bridge-darwin-arm64": "2.4.0", + "java-bridge-darwin-x64": "2.4.0", + "java-bridge-linux-arm64-gnu": "2.4.0", + "java-bridge-linux-x64-gnu": "2.4.0", + "java-bridge-win32-ia32-msvc": "2.4.0", + "java-bridge-win32-x64-msvc": "2.4.0" }, "dependencies": { "brace-expansion": { @@ -12519,67 +11546,66 @@ } }, "glob": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.1.tgz", - "integrity": "sha512-ngom3wq2UhjdbmRE/krgkD8BQyi1KZ5l+D2dVm4+Yj+jJIBp74/ZGunL6gNGc/CYuQmvUBiavWEXIotRiv5R6A==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", "requires": { "foreground-child": "^3.1.0", - "fs.realpath": "^1.0.0", "jackspeak": "^2.0.3", - "minimatch": "^9.0.0", - "minipass": "^5.0.0", - "path-scurry": "^1.7.0" + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" } }, "minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "requires": { "brace-expansion": "^2.0.1" } }, "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==" } } }, "java-bridge-darwin-arm64": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-darwin-arm64/-/java-bridge-darwin-arm64-2.3.0.tgz", - "integrity": "sha512-jIEly/4h0zZtRv1KA95kg8H4RVp7KUa+rn6bpLupfB/9uVtcM8uf1bFurvZRkGM41JYbM+y7KcotoKTFUgUKXA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-darwin-arm64/-/java-bridge-darwin-arm64-2.4.0.tgz", + "integrity": "sha512-dIFMV6w+lXnOU6xaeOmKsUOloIAzm577mkmi7Cim3Lue2nLZUFhDbwLiHTcnUTbq5+d7Qb8JbkdNTE2AjaPLmg==", "optional": true }, "java-bridge-darwin-x64": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-darwin-x64/-/java-bridge-darwin-x64-2.3.0.tgz", - "integrity": "sha512-ukhU321HSsofgEfnLm9QUbG09U3shwxulRVqIpKIalB+GB+HRF1Op8FzyRJOkA0Jzc0O36uq7sVQ7HBGEtF76A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-darwin-x64/-/java-bridge-darwin-x64-2.4.0.tgz", + "integrity": "sha512-2Fj9DUynyP6Wt6ceAmeCgkSaUkW20bOtRN/0JTqQe2wwFVGafFxzUjWUYygHlGG26Gtik3kJzic+c7Sxr1lFUQ==", "optional": true }, "java-bridge-linux-arm64-gnu": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-linux-arm64-gnu/-/java-bridge-linux-arm64-gnu-2.3.0.tgz", - "integrity": "sha512-ITV+7aFle1ucNhoREOGpem/ez8sCjePKsd+GC/JQ4wBLqNuPhfbvu7qCSHJiB9c/FeRUiLTh9NaBfHyuip92ng==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-linux-arm64-gnu/-/java-bridge-linux-arm64-gnu-2.4.0.tgz", + "integrity": "sha512-5IyZ5t6JDxEgjsOazhS+ZyBFjpSKN2agNFuCuW+R8wQmZMj459kXhsIZEHujtp7MobmMXc1dTcwaV8n3mqaR1w==", "optional": true }, "java-bridge-linux-x64-gnu": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-linux-x64-gnu/-/java-bridge-linux-x64-gnu-2.3.0.tgz", - "integrity": "sha512-Nzk97+9sOR4gMJJPYZd/o9OgkkhuWW5BaxSOmBxAhyZk4O6hha5T2w3QtvrqcfkbpyOY8yeaZdGq3k0n86ALnA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-linux-x64-gnu/-/java-bridge-linux-x64-gnu-2.4.0.tgz", + "integrity": "sha512-9V6kRmEbX1FFGqzRWr+NK9jxf9otiw9wCuLBoLBj/iErAjQpia6l061Z7vydt5c4Yu/lk107oBUo/Hsbtc/syQ==", "optional": true }, "java-bridge-win32-ia32-msvc": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-win32-ia32-msvc/-/java-bridge-win32-ia32-msvc-2.3.0.tgz", - "integrity": "sha512-MG4R1OSiCq20a1a/w4Hf5NtnNIulHhc0DnqomS/s09nH+xcVcDV9eQQ12pa7FoCZtK7nkVRwV/xhLQ8wtcK6zg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-win32-ia32-msvc/-/java-bridge-win32-ia32-msvc-2.4.0.tgz", + "integrity": "sha512-D+Im+AAiPgbT0BVWXbuSVQcbek4+VK6SHUVCXF4Gi02Yew/SJ+aNgIIkjP0TMVeYrxG1AlwcYQ80ahuzOm1acA==", "optional": true }, "java-bridge-win32-x64-msvc": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/java-bridge-win32-x64-msvc/-/java-bridge-win32-x64-msvc-2.3.0.tgz", - "integrity": "sha512-dDDD/+plvden+VHA2Zr5DebGDFnO15wpp+Udhn/XZjUBmfDto03BI814IsfTziDv0h/GDZIXVxvAnjtuQyBkGw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/java-bridge-win32-x64-msvc/-/java-bridge-win32-x64-msvc-2.4.0.tgz", + "integrity": "sha512-gK43rdXS6w7pNkz7DDHFnpj4A9v5yu5HPdFU4PankuQzlvLC5r4/S1hBXR4jvI5+md9c8LnvLaxLQwQ+OUfE8A==", "optional": true }, "joi": { @@ -12689,51 +11715,6 @@ "asn1.js": "^5.4.1" } }, - "lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "requires": { - "readable-stream": "^2.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -12751,11 +11732,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" - }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -12790,21 +11766,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -12823,11 +11784,6 @@ "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -12841,26 +11797,6 @@ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, - "lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=" - }, - "lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=" - }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" - }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, "log4js": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", @@ -12935,39 +11871,7 @@ "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "memory-stream": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", - "integrity": "sha1-6+jdHDuLw4wOeUHp3dWuvmtN6D8=", - "requires": { - "readable-stream": "~1.0.26-2" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, "meow": { "version": "8.1.2", @@ -13099,11 +12003,6 @@ "mime-db": "1.52.0" } }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" - }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -13126,7 +12025,8 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "minimist-options": { "version": "4.1.0", @@ -13169,11 +12069,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -13392,45 +12287,56 @@ } }, "mongodb": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz", - "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "peer": true, "requires": { - "@aws-sdk/credential-providers": "^3.186.0", - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.5.4", - "saslprep": "^1.0.3", - "socks": "^2.7.1" + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "dependencies": { + "bson": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", + "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", + "peer": true + } } }, "mongodb-connection-string-url": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.4.tgz", - "integrity": "sha512-SeAxuWs0ez3iI3vvmLk/j2y+zHwigTDKQhtdxTgt5ZCOQQS5+HW4g45/Xw5vzzbn7oQXCNQ24Z40AkJsizEy7w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "peer": true, "requires": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" }, "dependencies": { "tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "peer": true, "requires": { - "punycode": "^2.1.1" + "punycode": "^2.3.0" } }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "peer": true }, "whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "peer": true, "requires": { - "tr46": "^3.0.0", + "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" } } @@ -13468,32 +12374,142 @@ "yauzl": "^2.10.0" }, "dependencies": { + "@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true + }, + "mongodb": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", + "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", + "dev": true, + "requires": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + } + }, + "mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dev": true, + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } } } }, "mongoose": { - "version": "6.11.6", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.11.6.tgz", - "integrity": "sha512-CuVbeJrEbnxkPUNNFvXJhjVyqa5Ip7lkz6EJX6g7Lb3aFMTJ+LHOlUrncxzC3r20dqasaVIiwcA6Y5qC8PWQ7w==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.12.3.tgz", + "integrity": "sha512-MNJymaaXali7w7rHBxVUoQ3HzHHMk/7I/+yeeoSa4rUzdjZwIWQznBNvVgc0A8ghuJwsuIkb5LyLV6gSjGjWyQ==", "requires": { "bson": "^4.7.2", "kareem": "2.5.1", - "mongodb": "4.16.0", + "mongodb": "4.17.1", "mpath": "0.9.0", "mquery": "4.0.3", "ms": "2.1.3", "sift": "16.0.1" }, "dependencies": { + "@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "mongodb": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.1.tgz", + "integrity": "sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==", + "requires": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + } + }, + "mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } } } }, @@ -13530,11 +12546,6 @@ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -13562,21 +12573,6 @@ "debug": "^4.3.4" } }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "requires": { - "semver": "^5.4.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" - } - } - }, "node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -13623,7 +12619,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "npmlog": { "version": "5.0.1", @@ -13636,11 +12633,6 @@ "set-blocking": "^2.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -13694,14 +12686,6 @@ "word-wrap": "^1.2.3" } }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -13726,6 +12710,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13840,23 +12829,23 @@ "dev": true }, "path-scurry": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", - "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "requires": { - "lru-cache": "^9.0.0", - "minipass": "^5.0.0" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { "lru-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.0.tgz", - "integrity": "sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ==" + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==" }, "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==" } } }, @@ -13899,6 +12888,83 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "pg": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.1.tgz", + "integrity": "sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-cloudflare": "^1.1.1", + "pg-connection-string": "^2.6.1", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + }, + "pg-hstore": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.4.tgz", + "integrity": "sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA==", + "requires": { + "underscore": "^1.13.1" + } + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "requires": {} + }, + "pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + }, + "dependencies": { + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + } + } + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -13920,129 +12986,27 @@ "find-up": "^4.0.0" } }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } + "xtend": "^4.0.0" } }, "prelude-ls": { @@ -14055,7 +13019,8 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "proxy-addr": { "version": "2.0.7", @@ -14066,19 +13031,15 @@ "ipaddr.js": "1.9.1" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "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==" }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "q": { "version": "1.5.1", @@ -14094,6 +13055,14 @@ "side-channel": "^1.0.4" } }, + "queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "requires": { + "inherits": "~2.0.3" + } + }, "quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -14140,17 +13109,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -14259,14 +13217,6 @@ "util-deprecate": "^1.0.1" } }, - "readdir-glob": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz", - "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", - "requires": { - "minimatch": "^3.0.4" - } - }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -14356,6 +13306,11 @@ "dev": true, "peer": true }, + "retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, "rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", @@ -14384,15 +13339,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -14443,6 +13389,59 @@ } } }, + "sequelize": { + "version": "6.32.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.32.1.tgz", + "integrity": "sha512-3Iv0jruv57Y0YvcxQW7BE56O7DC1BojcfIrqh6my+IQwde+9u/YnuYHzK+8kmZLhLvaziRT1eWu38nh9yVwn/g==", + "requires": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.4", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.0", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.1", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "sequelize-erd": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/sequelize-erd/-/sequelize-erd-1.3.1.tgz", + "integrity": "sha512-w5/gNkj0WTp80KMvMxrrTp3HyIn1B8F5XmTXP6PXQNoWi1HKazXe9IvlbmGVP2yxx/YTtX+QWatcwkDk8HLK9Q==", + "dev": true, + "requires": { + "commander": "^2.9.0", + "lodash": "^4.17.15" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==" + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -14468,88 +13467,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "sharp": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz", - "integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==", - "requires": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - } - }, - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - }, - "node-abi": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", - "integrity": "sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==", - "requires": { - "semver": "^7.3.5" - } - }, - "node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "requires": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -14588,29 +13510,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -14639,8 +13538,7 @@ "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", - "optional": true, + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "requires": { "memory-pager": "^1.0.2" } @@ -14695,11 +13593,6 @@ "readable-stream": "^3.0.0" } }, - "splitargs": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", - "integrity": "sha1-/p965lc3GzOxDLgNoUPPgknPazs=" - }, "standard-version": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", @@ -14849,6 +13742,16 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "stringify-package": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", @@ -14863,6 +13766,14 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -14878,11 +13789,6 @@ "min-indent": "^1.0.0" } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, "strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", @@ -14963,28 +13869,11 @@ "yallist": "^4.0.0" } }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - }, - "dependencies": { - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - } - } - }, "tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, "requires": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -14997,6 +13886,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -15047,16 +13937,16 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" - }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -15069,14 +13959,6 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "devOptional": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -15122,6 +14004,14 @@ "dev": true, "optional": true }, + "uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "requires": { + "@lukeed/csprng": "^1.0.0" + } + }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -15130,6 +14020,11 @@ "random-bytes": "~1.0.0" } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -15140,53 +14035,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "unzipper": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", - "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", - "requires": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "~1.0.10", - "listenercount": "~1.0.1", - "readable-stream": "~2.1.5", - "setimmediate": "~1.0.4" - }, - "dependencies": { - "bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "readable-stream": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", - "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", - "requires": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -15197,11 +14045,6 @@ "punycode": "^2.1.0" } }, - "url-join": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", - "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -15213,9 +14056,9 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8-compile-cache": { "version": "2.3.0", @@ -15237,8 +14080,7 @@ "validator": { "version": "13.9.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "dev": true + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" }, "vary": { "version": "1.1.2", @@ -15259,14 +14101,6 @@ "webidl-conversions": "^3.0.0" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -15275,10 +14109,13 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + "wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "requires": { + "@types/node": "*" + } }, "word-wrap": { "version": "1.2.5", @@ -15299,46 +14136,14 @@ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { @@ -15355,13 +14160,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "yallist": { "version": "4.0.0", @@ -15374,53 +14173,6 @@ "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", "dev": true }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "requires": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, "yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", @@ -15489,16 +14241,6 @@ "optional": true } } - }, - "zip-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", - "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", - "requires": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" - } } } } diff --git a/package.json b/package.json index 85cbc202..08920a44 100644 --- a/package.json +++ b/package.json @@ -54,28 +54,20 @@ "keywords": [], "author": "chinlinlee", "license": "MIT", - "_moduleAliases": { - "@dcm4che": "./models/DICOM/dcm4che/wrapper/org/dcm4che3", - "@java-wrapper": "./models/DICOM/dcm4che/wrapper", - "@models": "./models", - "@error": "./error", - "@root": "./", - "@chinlinlee": "./models/DICOM/dcm4che/wrapper/org/github/chinlinlee" - }, "dependencies": { "@jorgeferrero/stream-to-buffer": "^2.0.6", - "archiver": "^5.3.1", - "axios": "^0.26.1", + "7zip-min": "^1.4.4", + "axios": "^1.6.2", "bcrypt": "^5.0.1", "body-parser": "^1.20.0", "colorette": "^2.0.20", "commander": "^10.0.1", "compression": "^1.7.4", - "connect-mongo": "^4.6.0", + "connect-mongo": "^5.1.0", + "connect-session-sequelize": "^7.1.7", "cookie-parser": "^1.4.6", "cors": "^2.8.5", - "dicom-parser": "^1.8.13", - "dicom-to-json": "^1.2.1", + "dicomjson-to-fhir": "^1.0.1", "dotenv": "^16.0.3", "env-var": "^7.3.1", "express": "^4.18.2", @@ -83,6 +75,7 @@ "flat": "^5.0.2", "formidable": "^2.0.1", "iconv-lite": "^0.6.3", + "image-size": "^1.1.1", "imagemagick-cli": "^0.5.0", "java-bridge": "^2.3.0", "joi": "^17.6.0", @@ -98,13 +91,15 @@ "passport": "^0.6.0", "passport-local": "^1.0.0", "path-match": "^1.2.4", + "pg": "^8.11.1", + "pg-hstore": "^2.3.4", "regexparam": "^2.0.1", "request-compose": "^2.1.6", "request-multipart": "^1.0.0", "run-script-os": "^1.1.6", - "sharp": "^0.30.4", + "sequelize": "^6.32.1", "shorthash2": "^1.0.3", - "uuid": "^9.0.0", + "uuid": "^9.0.1", "ws": "^8.13.0" }, "devDependencies": { @@ -112,6 +107,7 @@ "eslint-config-prettier": "^8.5.0", "mocha": "^10.2.0", "mongodb-memory-server": "^8.12.2", + "sequelize-erd": "^1.3.1", "standard-version": "^9.5.0", "swagger-jsdoc": "^6.2.8" } diff --git a/plugins/config.template.js b/plugins/config.template.js index c53654d0..ecd56b1a 100644 --- a/plugins/config.template.js +++ b/plugins/config.template.js @@ -12,5 +12,20 @@ module.exports.pluginsConfig = { method: "get" } ] + }, + "syncToFhirServer": { + enable: false, + before: false, + routers: [ + { + path: "/dicom-web/studies", + method: "post" + } + ], + fhir: { + server: { + baseUrl: "http://127.0.0.1/fhir" + } + } } }; \ No newline at end of file diff --git a/models/FHIR/DICOM/DICOMToFHIR.js b/plugins/syncToFhirServer/DICOMToFHIR.js similarity index 91% rename from models/FHIR/DICOM/DICOMToFHIR.js rename to plugins/syncToFhirServer/DICOMToFHIR.js index f3589431..3013c1f8 100644 --- a/models/FHIR/DICOM/DICOMToFHIR.js +++ b/plugins/syncToFhirServer/DICOMToFHIR.js @@ -1,14 +1,12 @@ const { URL } = require("url"); const axios = require("axios").default; const _ = require("lodash"); -const { urlJoin } = require("../../../utils/url"); -const { fhirLogger } = require("../../../utils/logs/log"); -const { dicomJsonToFHIRImagingStudy } = require("./DICOMToFHIRImagingStudy"); -const { dicomJsonToFHIRPatient } = require("./DICOMToFHIRPatient"); -const { dicomJsonToFHIREndpoint } = require("./DICOMToFHIREndpoint"); -const { getModalitiesInStudy } = require("../../../models/mongodb/models/dicom"); +const { urlJoin } = require("../../utils/url"); +const { fhirLogger } = require("../../utils/logs/log"); +const { getModalitiesInStudy } = require("@dbModels/instance.model"); +const { DicomJsonToFhir } = require("dicomjson-to-fhir"); -class DICOMFHIRConverter { +class DicomFhirConverter { constructor() { this.dicomFHIR = { patient: {}, @@ -32,19 +30,15 @@ class DICOMFHIRConverter { * @param {JSON} dicomJson The DICOM Json model */ async dicomJsonToFHIR(dicomJson) { - let patient = dicomJsonToFHIRPatient(dicomJson); - let imagingStudy = dicomJsonToFHIRImagingStudy(dicomJson); - if (!imagingStudy.subject.reference.includes(patient.id)) { - imagingStudy.subject.reference = `Patient/${patient.id}`; - } - let endpoint = dicomJsonToFHIREndpoint( + this.dicomFHIR = new DicomJsonToFhir( + dicomJson, this.dicomWeb.retrieveStudiesUrl, this.dicomWeb.name - ); - this.dicomFHIR.patient = patient; - this.dicomFHIR.imagingStudy = imagingStudy; + ).getFhirJson(); + if (!this.dicomFHIR.imagingStudy.subject.reference.includes(this.dicomFHIR.patient.id)) { + this.dicomFHIR.imagingStudy.subject.reference = `Patient/${this.dicomFHIR.patient.id}`; + } await this.setModalitiesInStudy(dicomJson); - this.dicomFHIR.endpoint = endpoint; this.dicomFHIR.imagingStudy.endpoint = [ { reference: `Endpoint/${this.dicomFHIR.endpoint.id}`, @@ -61,7 +55,7 @@ class DICOMFHIRConverter { }); if (modalitiesInStudy.length > 0) { let modalitiesInStudyValue = modalitiesInStudy[0]["00080061"].Value; - for(let i = 0 ; i < modalitiesInStudyValue.length; i++) { + for (let i = 0; i < modalitiesInStudyValue.length; i++) { let modality = { system: "http://dicom.nema.org/resources/ontology/DCM", code: modalitiesInStudyValue[i] @@ -335,4 +329,4 @@ class DICOMFHIRConverter { } } -module.exports.DICOMFHIRConverter = DICOMFHIRConverter; +module.exports.DicomFhirConverter = DicomFhirConverter; diff --git a/api/dicom-web/controller/STOW-RS/service/dicom-fhir.service.js b/plugins/syncToFhirServer/dicom-fhir.service.js similarity index 73% rename from api/dicom-web/controller/STOW-RS/service/dicom-fhir.service.js rename to plugins/syncToFhirServer/dicom-fhir.service.js index c86f2740..3837fce1 100644 --- a/api/dicom-web/controller/STOW-RS/service/dicom-fhir.service.js +++ b/plugins/syncToFhirServer/dicom-fhir.service.js @@ -1,35 +1,46 @@ +const path = require("path"); +const fs = require("fs"); + const mongoose = require("mongoose"); const { - DICOMFHIRConverter -} = require("../../../../../models/FHIR/DICOM/DICOMToFHIR"); -const { fhirLogger } = require("../../../../../utils/logs/log"); + DicomFhirConverter +} = require("./DICOMToFHIR"); +const { fhirLogger } = require("../../utils/logs/log"); -const { raccoonConfig } = require("../../../../../config-class"); +const { raccoonConfig } = require("../../config-class"); const { apiPath: DICOM_WEB_API_PATH } = raccoonConfig.dicomWebConfig; -const { - baseUrl: FHIR_BASE_URL -} = raccoonConfig.fhirConfig; + +let pluginConfigFile = path.join(__dirname, "../config.template.js"); +if (fs.existsSync(path.join(__dirname, "../config.js"))) { + pluginConfigFile = path.join(__dirname, "../config.js"); +} + +const fhirBaseUrl = require(pluginConfigFile).pluginsConfig?.syncToFhirServer?.fhir?.server?.baseUrl; class DicomFhirService { constructor(req, dicomJsonModel) { + if (!fhirBaseUrl) { + throw new Error("missing fhir config in your plugin config"); + } + this.request = req; this.dicomJsonModel = dicomJsonModel; /** * @private */ - this.dicomFhirConverter = new DICOMFHIRConverter(); + this.dicomFhirConverter = new DicomFhirConverter(); } async initDicomFhirConverter() { this.dicomFhirConverter.dicomWeb.name =`raccoon-dicom-web-server`; let protocol = this.request.secure ? "https" : "http"; this.dicomFhirConverter.dicomWeb.retrieveStudiesUrl = `${protocol}://${this.request.headers.host}/${DICOM_WEB_API_PATH}/studies`; - this.dicomFhirConverter.fhir.baseUrl = FHIR_BASE_URL; + this.dicomFhirConverter.fhir.baseUrl = fhirBaseUrl; } async postDicomToFhirServerAndStoreLog() { @@ -41,7 +52,7 @@ class DicomFhirService { let logObj = { studyUID: this.dicomJsonModel.uidObj.studyUID, seriesUID: this.dicomJsonModel.uidObj.seriesUID, - instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID, + instanceUID: this.dicomJsonModel.uidObj.instanceUID, status: true, message: "success" }; @@ -56,7 +67,7 @@ class DicomFhirService { let errorLogObj = { studyUID: this.dicomJsonModel.uidObj.studyUID, seriesUID: this.dicomJsonModel.uidObj.seriesUID, - instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID, + instanceUID: this.dicomJsonModel.uidObj.instanceUID, status: false, message: errorStr }; @@ -81,7 +92,7 @@ class DicomFhirService { seriesUID: this.dicomJsonModel.uidObj.seriesUID }, { - instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID + instanceUID: this.dicomJsonModel.uidObj.instanceUID } ] }, diff --git a/plugins/syncToFhirServer/index.js b/plugins/syncToFhirServer/index.js new file mode 100644 index 00000000..85f64796 --- /dev/null +++ b/plugins/syncToFhirServer/index.js @@ -0,0 +1,30 @@ +require("./mongoose/syncFHIRLog"); +const path = require("path"); +const fs = require("fs"); +const { DicomFhirService } = require("./dicom-fhir.service"); +let pluginConfigFile = path.join(__dirname, "../config.template.js"); + +if (fs.existsSync(path.join(__dirname, "../config.js"))) pluginConfigFile = path.join(__dirname, "../config.js"); + +const { pluginsConfig } = require(pluginConfigFile); + +/** + * + * @param {import("express").Request} req + * @param {import("express").Response} res + * @returns + */ +module.exports = async function (req, res) { + if (!pluginsConfig?.syncToFhirServer?.enable) return; + + setImmediate(async () => { + for (let i = 0; i < res.locals.storeInfos.length; i++) { + /** @type { import("./storeInfo").StoreInfo } */ + let storeInfo = res.locals.storeInfos[i]; + let dicomFhirService = new DicomFhirService(req, storeInfo.dicomJsonModel); + await dicomFhirService.initDicomFhirConverter(); + await dicomFhirService.postDicomToFhirServerAndStoreLog(); + } + }); + +}; \ No newline at end of file diff --git a/models/mongodb/models/syncFHIRLog.js b/plugins/syncToFhirServer/mongoose/syncFHIRLog.js similarity index 100% rename from models/mongodb/models/syncFHIRLog.js rename to plugins/syncToFhirServer/mongoose/syncFHIRLog.js diff --git a/plugins/syncToFhirServer/storeInfo.d.ts b/plugins/syncToFhirServer/storeInfo.d.ts new file mode 100644 index 00000000..e73e0f91 --- /dev/null +++ b/plugins/syncToFhirServer/storeInfo.d.ts @@ -0,0 +1,7 @@ +import type { DicomJsonModel } from "@models/DICOM/dicom-json-model" +import type { DicomFileSaveInfo } from "@root/utils/typeDef/STOW-RS/STOW-RS" + +export type StoreInfo = { + dicomFileSaveInfo: DicomFileSaveInfo, + dicomJsonModel: DicomJsonModel +} \ No newline at end of file diff --git a/routes.js b/routes.js index a994920c..c11fe1e4 100644 --- a/routes.js +++ b/routes.js @@ -28,6 +28,10 @@ module.exports = function (app) { app.use("/dicom-web", require("./api/dicom-web/wado-rs-thumbnail.route")); app.use("/dicom-web", require("./api/dicom-web/delete.route")); app.use("/dicom-web", require("./api/dicom-web/ups-rs.route")); + app.use("/dicom-web", require("./api/dicom-web/mwl-rs.route")); + app.use("/dicom-web", require("./api/dicom-web/pam-rs.route")); app.use("/wado", require("./api/WADO-URI")); + + app.use("/fhir-convert", require("./api/fhir-convert")); }; diff --git a/server.js b/server.js index 0fb6fc1b..e5368cdd 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,7 @@ RegExp.prototype.toJSON = RegExp.prototype.toString; -require('module-alias')(__dirname); +const { raccoonConfig } = require("./config-class"); + +require('module-alias')(__dirname + "/config/modula-alias/mongodb"); const { app, server } = require("./app"); const bodyParser = require("body-parser"); @@ -8,12 +10,16 @@ const cookieParser = require("cookie-parser"); const compress = require("compression"); const cors = require("cors"); const os = require("os"); -const mongoose = require("mongoose"); -const MongoStore = require("connect-mongo"); + +let sessionStore = require("connect-mongo");; +let dbInstance = require("mongoose"); +let sessionStoreOption = sessionStore.create({ + client: dbInstance.connection.getClient(), + dbName: raccoonConfig.dbConfig.dbName +}); const passport = require("passport"); -const { raccoonConfig } = require("./config-class"); -const { DcmQrScp } = require('./dimse'); +const { DcmQrScp } = require('@dimse'); require("dotenv"); require("./websocket"); @@ -42,6 +48,7 @@ app.use( //#region session + app.use( session({ secret: raccoonConfig.serverConfig.secretKey || "secretKey", @@ -51,10 +58,7 @@ app.use( httpOnly: true, maxAge: 60 * 60 * 1000 }, - store: MongoStore.create({ - client: mongoose.connection.getClient(), - dbName: raccoonConfig.mongoDbConfig.dbName - }) + store: sessionStoreOption }) ); diff --git a/test/QIDO-RS-Service/common.test.js b/test/QIDO-RS-Service/common.test.js index d9e9d430..8462de7c 100644 --- a/test/QIDO-RS-Service/common.test.js +++ b/test/QIDO-RS-Service/common.test.js @@ -1,13 +1,13 @@ const mongoose = require("mongoose"); -const patientModel = require("../../models/mongodb/models/patient"); +const patientModel = require("../../models/mongodb/models/patient.model"); const { DicomJsonModel } = require("../../models/DICOM/dicom-json-model"); const { expect } = require("chai"); const _ = require("lodash"); -const { - convertAllQueryToDICOMTag, - convertRequestQueryToMongoQuery -} = require("../../api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); + const moment = require("moment"); +const { convertRequestQueryToMongoQuery } = require("@models/mongodb/convertQuery"); +const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); + describe("QIDO-RS Service Common Function", () => { @@ -41,7 +41,7 @@ describe("QIDO-RS Service Common Function", () => { */ it("Should convert `00100010=foobar1234`, VR: `PN` to Mongo query", async ()=> { - let query = convertAllQueryToDICOMTag({ + let query = convertAllQueryToDicomTag({ "00100010": "foobar1234" }); @@ -81,7 +81,7 @@ describe("QIDO-RS Service Common Function", () => { describe("Convert `Date` VR: `DA` query", ()=> { it("Should convert `00100030=19991111` to Mongo query", async ()=> { - let query = convertAllQueryToDICOMTag({ + let query = convertAllQueryToDicomTag({ "00100030": "19991111" }); @@ -108,7 +108,7 @@ describe("QIDO-RS Service Common Function", () => { }); it("Should convert `00100030=19991111-` to Mongo query", async ()=> { - let query = convertAllQueryToDICOMTag({ + let query = convertAllQueryToDicomTag({ "00100030": "19991111-" }); @@ -134,7 +134,7 @@ describe("QIDO-RS Service Common Function", () => { }); it("Should convert `00100030=-19991111` to Mongo query", async ()=> { - let query = convertAllQueryToDICOMTag({ + let query = convertAllQueryToDicomTag({ "00100030": "-19991111" }); @@ -160,7 +160,7 @@ describe("QIDO-RS Service Common Function", () => { }); it("Should convert `00100030=19900101-19991111` to Mongo query", async ()=> { - let query = convertAllQueryToDICOMTag({ + let query = convertAllQueryToDicomTag({ "00100030": "19900101-19991111" }); @@ -190,7 +190,7 @@ describe("QIDO-RS Service Common Function", () => { describe("Convert string `00100020=foobar` VR: `LO`", () => { it("Should convert string completely", async ()=> { - let query = convertAllQueryToDICOMTag({ + let query = convertAllQueryToDicomTag({ "00100020": "foobar" }); diff --git a/test/QIDO-RS-Service/patient.test.js b/test/QIDO-RS-Service/patient.test.js index 897a8ea9..0caca936 100644 --- a/test/QIDO-RS-Service/patient.test.js +++ b/test/QIDO-RS-Service/patient.test.js @@ -1,22 +1,19 @@ const mongoose = require("mongoose"); -const patientModel = require("../../models/mongodb/models/patient"); +const patientModel = require("../../models/mongodb/models/patient.model"); const { DicomJsonModel } = require("../../models/DICOM/dicom-json-model"); const { expect } = require("chai"); const _ = require("lodash"); -const { - QidoDicomJsonFactory, - convertAllQueryToDICOMTag, - convertRequestQueryToMongoQuery -} = require("../../api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); +const { QueryPatientDicomJsonFactory } = require("../../api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory"); +const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); -describe("Patient QIDO-RS Service", async() => { +describe("Patient QIDO-RS Service", async () => { let fakePatientData = { "patientID": "foobar123456", "00100010": { "vr": "PN", "Value": [ { - "Alphabetic" : "John^Doe" + "Alphabetic": "John^Doe" } ] }, @@ -85,7 +82,7 @@ describe("Patient QIDO-RS Service", async() => { } }; - before(async() => { + before(async () => { let cloneFakePatientData = _.cloneDeep(fakePatientData); _.set(cloneFakePatientData, "studyPath", "/foo/bar"); let dicomJsonModel = new DicomJsonModel(cloneFakePatientData); @@ -95,21 +92,21 @@ describe("Patient QIDO-RS Service", async() => { await dicomJsonModel.storePatientCollection(cloneFakePatientData); }); - describe("Query `PatientID (0010, 0020)` using `QidoDicomJsonFactory`", () => { + describe("Query `PatientID (0010, 0020)` using `QueryPatientDicomJsonFactory`", () => { it("Should search PatientID=`foobar123456` patient and return 1", async () => { let q = { "00100020": "foobar123456" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryPatientDicomJsonFactory({ query: { ...q }, limit: 10, skip: 0 - }, "patient"); - let patientJson = await qidoDicomJsonFactory.getDicomJson(); + }); + let patientJson = await dicomJsonFactory.getDicomJson(); expect(patientJson).is.an("array").length(1); }); @@ -117,36 +114,36 @@ describe("Patient QIDO-RS Service", async() => { let q = { "00100020": "foobar123" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryPatientDicomJsonFactory({ query: { ...q }, limit: 10, skip: 0 - }, "patient"); - let patientJson = await qidoDicomJsonFactory.getDicomJson(); + }); + let patientJson = await dicomJsonFactory.getDicomJson(); expect(patientJson).is.an("array").length(0); }); }); - describe("Query `PatientName (0010,0010)` using `QidoDicomJsonFactory`", () => { + describe("Query `PatientName (0010,0010)` using `QueryPatientDicomJsonFactory`", () => { it("Should search PatientName=`John*` patient and return 1", async () => { let q = { "00100010": "John*" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryPatientDicomJsonFactory({ query: { ...q }, limit: 10, skip: 0 - }, "patient"); - let patientJson = await qidoDicomJsonFactory.getDicomJson(); + }); + let patientJson = await dicomJsonFactory.getDicomJson(); expect(patientJson).is.an("array").length(2); }); @@ -154,23 +151,23 @@ describe("Patient QIDO-RS Service", async() => { let q = { "00100010": "John Doe" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryPatientDicomJsonFactory({ query: { ...q }, limit: 10, skip: 0 - }, "patient"); - let patientJson = await qidoDicomJsonFactory.getDicomJson(); + }); + let patientJson = await dicomJsonFactory.getDicomJson(); expect(patientJson).is.an("array").length(0); }); }); - - after(async()=> { + + after(async () => { await patientModel.deleteOne({ patientID: "foobar123456" }); diff --git a/test/before.js b/test/before.js index e272a9ab..3a9bb891 100644 --- a/test/before.js +++ b/test/before.js @@ -1,4 +1,4 @@ -require("module-alias/register"); +require('module-alias')(__dirname + "/../config/modula-alias/mongodb"); const mongoose = require("mongoose"); const { MongoMemoryServer } = require("mongodb-memory-server"); const {getLogger} = require("log4js"); diff --git a/test/delete.test.js b/test/delete.test.js index c3383a9a..1ac74bd4 100644 --- a/test/delete.test.js +++ b/test/delete.test.js @@ -1,7 +1,7 @@ const mongoose = require("mongoose"); const { expect } = require("chai"); -const { QidoDicomJsonFactory } = require("../api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); const { DeleteService } = require("../api/dicom-web/controller/WADO-RS/deletion/service/delete"); +const { QueryInstanceDicomJsonFactory, QuerySeriesDicomJsonFactory, QueryStudyDicomJsonFactory } = require("../api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory"); const studyInstanceUid = "1.3.6.1.4.1.14519.5.2.1.7085.2626.192997540292073877946622133586"; @@ -46,7 +46,7 @@ describe("Delete DICOM Instances by SOPInstanceUID", async () => { it("Should delete instance and expect 4 instances in series", async function () { await deleteBySopInstanceUid(); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryInstanceDicomJsonFactory({ query: {}, requestParams: { studyUID: studyInstanceUid, @@ -54,9 +54,9 @@ describe("Delete DICOM Instances by SOPInstanceUID", async () => { }, limit: 100, skip: 0 - }, "instance"); + }); - let dicomJson = await qidoDicomJsonFactory.getDicomJson(); + let dicomJson = await dicomJsonFactory.getDicomJson(); expect(dicomJson).have.lengthOf(4); }); @@ -67,16 +67,16 @@ describe("Delete DICOM Instances by SeriesInstanceUID", async () => { it("Should delete series and expect 2 series in study", async function (){ await deleteBySeriesInstanceUid(); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QuerySeriesDicomJsonFactory({ query: {}, requestParams: { studyUID: studyInstanceUid }, limit: 100, skip: 0 - }, "series"); + }); - let dicomJson = await qidoDicomJsonFactory.getDicomJson(); + let dicomJson = await dicomJsonFactory.getDicomJson(); expect(dicomJson).have.lengthOf(2); }); @@ -87,14 +87,14 @@ describe("Delete DICOM Instances by StudyInstanceUID", async () => { it("Should delete study and expect 3 studies", async () => { await deleteByStudyInstanceUid(); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: {}, requestParams: {}, limit: 100, skip: 0 - }, "study"); + }); - let dicomJson = await qidoDicomJsonFactory.getDicomJson(); + let dicomJson = await dicomJsonFactory.getDicomJson(); expect(dicomJson).have.lengthOf(3); }); diff --git a/test/patient.mongo.test.js b/test/patient.mongo.test.js index 3e4e5ed1..5e040e44 100644 --- a/test/patient.mongo.test.js +++ b/test/patient.mongo.test.js @@ -1,5 +1,5 @@ const mongoose = require("mongoose"); -const patientModel = require("../models/mongodb/models/patient"); +const patientModel = require("../models/mongodb/models/patient.model"); const { DicomJsonModel } = require("../models/DICOM/dicom-json-model"); const { expect } = require("chai"); const _ = require("lodash"); @@ -88,6 +88,9 @@ describe("Patient MongoDB and DicomJsonModel", async() => { let docObj = findDoc.toObject(); delete docObj._id; delete docObj.id; + delete docObj.deleteStatus; + delete docObj.createdAt; + delete docObj.updatedAt; expect(docObj).to.deep.equal(fakePatientData); }); diff --git a/test/query.test.js b/test/query.test.js index 40a44a39..eb9f61de 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -3,13 +3,14 @@ const formidable = require("formidable"); const glob = require("glob"); const path = require("path"); const fsP = require("fs/promises"); -const { StowRsService } = require("../api/dicom-web/controller/STOW-RS/service/stow-rs.service"); -const patientModel = require("../models/mongodb/models/patient"); -const dicomStudyModel = require("../models/mongodb/models/dicomStudy"); -const dicomSeriesModel = require("../models/mongodb/models/dicomSeries"); -const dicomModel = require("../models/mongodb/models/dicom"); +const patientModel = require("../models/mongodb/models/patient.model"); +const dicomStudyModel = require("../models/mongodb/models/study.model"); +const dicomSeriesModel = require("../models/mongodb/models/series.model"); +const dicomModel = require("../models/mongodb/models/instance.model"); const { expect } = require("chai"); -const { QidoDicomJsonFactory, convertAllQueryToDICOMTag } = require("../api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); +const { QueryStudyDicomJsonFactory, QuerySeriesDicomJsonFactory, QueryInstanceDicomJsonFactory } = require("../api/dicom-web/controller/QIDO-RS/service/query-dicom-json-factory"); +const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); + describe("Query DICOM of study, series, and instance level", async () => { @@ -24,17 +25,17 @@ describe("Query DICOM of study, series, and instance level", async () => { "StudyDate": "19990101-19991231" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, limit: 100, skip: 0 - }, "study"); + }); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(2); }); @@ -43,17 +44,17 @@ describe("Query DICOM of study, series, and instance level", async () => { "StudyDate": "20220101-20221231" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, limit: 100, skip: 0 - }, "study"); + }); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(0); }); }); @@ -64,9 +65,9 @@ describe("Query DICOM of study, series, and instance level", async () => { "PatientID": "TCGA-G4-6304" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, @@ -74,7 +75,7 @@ describe("Query DICOM of study, series, and instance level", async () => { skip: 0 }, "study"); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); studyForSecondSeriesTest = studyDicomJson[0]; expect(studyDicomJson).is.an("array").have.lengthOf(1); }); @@ -87,9 +88,9 @@ describe("Query DICOM of study, series, and instance level", async () => { "StudyDate": "20100101-20101231" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, @@ -97,7 +98,7 @@ describe("Query DICOM of study, series, and instance level", async () => { skip: 0 }, "study"); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(0); }); @@ -107,9 +108,9 @@ describe("Query DICOM of study, series, and instance level", async () => { "StudyDate": "19990101-19991231" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, @@ -117,7 +118,7 @@ describe("Query DICOM of study, series, and instance level", async () => { skip: 0 }, "study"); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(1); }); }); @@ -129,17 +130,17 @@ describe("Query DICOM of study, series, and instance level", async () => { "PatientBirthDate": "19590101" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, limit: 100, skip: 0 - }, "study"); + }); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(0); }); @@ -149,17 +150,17 @@ describe("Query DICOM of study, series, and instance level", async () => { "PatientBirthDate": "19601218" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, limit: 100, skip: 0 - }, "study"); + }); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(1); }); }); @@ -171,9 +172,9 @@ describe("Query DICOM of study, series, and instance level", async () => { "AccessionNumber": "4444" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, @@ -181,7 +182,7 @@ describe("Query DICOM of study, series, and instance level", async () => { skip: 0 }, "study"); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); expect(studyDicomJson).is.an("array").have.lengthOf(0); }); @@ -191,17 +192,17 @@ describe("Query DICOM of study, series, and instance level", async () => { "AccessionNumber": "2794663908550664" }; - q = convertAllQueryToDICOMTag(q); + q = convertAllQueryToDicomTag(q); - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryStudyDicomJsonFactory({ query: { ...q }, limit: 100, skip: 0 - }, "study"); + }); - let studyDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let studyDicomJson = await dicomJsonFactory.getDicomJson(); studyForFirstSeriesTest = studyDicomJson[0]; expect(studyDicomJson).is.an("array").have.lengthOf(1); }); @@ -211,7 +212,7 @@ describe("Query DICOM of study, series, and instance level", async () => { describe("Search For Series", () => { it("(step 150): Should use StudyInstanceUID search series and expect 3 series", async () => { - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QuerySeriesDicomJsonFactory({ query: {}, requestParams: { studyUID: studyForFirstSeriesTest["0020000D"]["Value"][0] @@ -220,23 +221,23 @@ describe("Query DICOM of study, series, and instance level", async () => { skip: 0 }, "series"); - let seriesDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let seriesDicomJson = await dicomJsonFactory.getDicomJson(); seriesForFirstInstanceTest = seriesDicomJson; expect(seriesDicomJson).is.an("array").have.lengthOf(3); }); it("Should use StudyInstanceUID from step 170 search series and expect 3 series", async () => { - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QuerySeriesDicomJsonFactory({ query: {}, requestParams: { studyUID: studyForSecondSeriesTest["0020000D"]["Value"][0] }, limit: 100, skip: 0 - }, "series"); + }); - let seriesDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let seriesDicomJson = await dicomJsonFactory.getDicomJson(); expect(seriesDicomJson).is.an("array").have.lengthOf(3); }); }); @@ -245,7 +246,7 @@ describe("Query DICOM of study, series, and instance level", async () => { it("Should expect 1 image, 5 images, and 5 images in 3 series respectively from step 150's series", async () => { let seriesImageCount = []; for (let dicomJson of seriesForFirstInstanceTest) { - let qidoDicomJsonFactory = new QidoDicomJsonFactory({ + let dicomJsonFactory = new QueryInstanceDicomJsonFactory({ query: {}, requestParams: { studyUID: dicomJson["0020000D"]["Value"][0], @@ -253,9 +254,9 @@ describe("Query DICOM of study, series, and instance level", async () => { }, limit: 100, skip: 0 - }, "instance"); + }); - let instanceDicomJson = await qidoDicomJsonFactory.getDicomJson(); + let instanceDicomJson = await dicomJsonFactory.getDicomJson(); seriesImageCount.push(instanceDicomJson.length); } seriesImageCount.sort((a, b) => a - b); diff --git a/test/store-instances.test.js b/test/store-instances.test.js index 24236276..b89976a5 100644 --- a/test/store-instances.test.js +++ b/test/store-instances.test.js @@ -4,12 +4,11 @@ const glob = require("glob"); const path = require("path"); const fsP = require("fs/promises"); const { StowRsService } = require("../api/dicom-web/controller/STOW-RS/service/stow-rs.service"); -const patientModel = require("../models/mongodb/models/patient"); -const dicomStudyModel = require("../models/mongodb/models/dicomStudy"); -const dicomSeriesModel = require("../models/mongodb/models/dicomSeries"); -const dicomModel = require("../models/mongodb/models/dicom"); +const patientModel = require("../models/mongodb/models/patient.model"); +const dicomStudyModel = require("../models/mongodb/models/study.model"); +const dicomSeriesModel = require("../models/mongodb/models/series.model"); +const dicomModel = require("../models/mongodb/models/instance.model"); const { expect } = require("chai"); -const { QidoDicomJsonFactory, convertAllQueryToDICOMTag } = require("../api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service"); async function storeDicomInstancesAndGet4Patients() { let stowRsService = new StowRsService({ @@ -17,7 +16,7 @@ async function storeDicomInstancesAndGet4Patients() { host: "fake-host" }, params: {} - }, []); + }, { locals: {} }, []); /** @type {string[]} */ let files = glob.sync("**/*.dcm", { diff --git a/utils/multipartWriter.js b/utils/multipartWriter.js index 25d5963e..3764cb35 100644 --- a/utils/multipartWriter.js +++ b/utils/multipartWriter.js @@ -2,7 +2,6 @@ const { logger } = require("../utils/logs/log"); const uuid = require("uuid"); const fs = require("fs"); const _ = require("lodash"); -const dicomParser = require("dicom-parser"); const { streamToBuffer } = require("@jorgeferrero/stream-to-buffer"); const { Dcm2JpgExecutor } = require("../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor"); const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions"); @@ -147,57 +146,6 @@ class MultipartWriter { } } - /** - * Write image files of frames in multipart content - * @param {string} type - * @param {Array} frameList - * @returns - */ - async writeFrames(type, frameList) { - this.setHeaderMultipartRelatedContentType(); - let dicomFilename = `${this.imagePathObjList[0].instancePath}`; - let jpegFile = dicomFilename.replace(/\.dcm\b/gi, ""); - let minFrameNumber = _.min(frameList); - let maxFrameNumber = _.max(frameList); - let frameNumberCount = maxFrameNumber - minFrameNumber + 1; - if (minFrameNumber == maxFrameNumber) { - frameNumberCount = 1; - } - - try { - - for (let i = 1; i <= frameNumberCount; i++) { - /** @type {Dcm2JpgExecutor$Dcm2JpgOptions} */ - let opt = await Dcm2JpgExecutor$Dcm2JpgOptions.newInstanceAsync(); - opt.frameNumber = i; - await Dcm2JpgExecutor.convertDcmToJpgFromFilename(dicomFilename, `${jpegFile}.${i - 1}.jpg`, opt); - } - - for (let x = 0; x < frameList.length; x++) { - let frameJpegFile = dicomFilename.replace( - /\.dcm\b/gi, - `.${frameList[x] - 1}.jpg` - ); - let fileBuffer = fs.readFileSync(frameJpegFile); - let dicomFileBuffer = fs.readFileSync(dicomFilename); - let dicomDataSet = dicomParser.parseDicom(dicomFileBuffer, { - untilTag: "x7fe00010" - }); - let transferSyntax = dicomDataSet.string("x00020010"); - this.writeBoundary(); - this.writeContentType(type, transferSyntax); - this.writeContentLength(fileBuffer.length); - this.writeContentLocation(); - this.writeBufferData(fileBuffer); - } - this.writeFinalBoundary(); - return true; - } catch (e) { - logger.error(e); - return false; - } - } - writeBuffer(buffer, headers) { try { this.writeBoundary(); diff --git a/utils/sevenZip.js b/utils/sevenZip.js new file mode 100644 index 00000000..61de6c33 --- /dev/null +++ b/utils/sevenZip.js @@ -0,0 +1,86 @@ +const SevenZipMin = require("7zip-min"); + +class SevenZip { + + /** @type { import("@root/utils/typeDef/sevenZip").archiveType } */ + #type; + /** @type { string } */ + #source; + /** @type { string } */ + #dest; + constructor(type = "zip", source = "", dest = "") { + this.#type = type; + this.#source = source; + this.#dest = dest; + /** @type {string[]} */ + this.cmd = []; + } + + /** + * + * @param { import("@root/utils/typeDef/sevenZip").archiveType } value + */ + setType(value) { + this.#type = value; + return this; + } + + setSource(value) { + this.#source = value; + return this; + } + + setDest(value) { + this.#dest = value; + return this; + } + + addCmd(iCmd) { + this.cmd.push(iCmd); + return this; + } + + recursive() { + if (this.cmd.includes("-r")) throw new Error("already set recursive"); + this.cmd.push("-r"); + return this; + } + + useFullyQualifiedFilePaths() { + if (this.cmd.includes("-spf2")) throw new Error("already set useFullyQualifiedFilePaths"); + this.cmd.push("-spf2"); + return this; + } + + /** + * + * @param {import("@root/utils/typeDef/sevenZip").overwriteMode} mode + */ + overwrite(mode) { + if (this.cmd.find(v => v.startsWith("-ao"))) throw new Error("already set overwrite"); + this.cmd.push(`-ao${mode}`); + return this; + } + + /** + * run 7zip command: + * + * 7z a -t{type} {dest} {source} {...additionalCmd} + * @returns + */ + async pack() { + return new Promise((resolve, reject) => { + let cmd = ["a", `-t${this.#type}`, this.#dest, this.#source, ...this.cmd]; + if (!this.#source) cmd = ["a", `-t${this.#type}`, this.#dest, ...this.cmd]; + SevenZipMin.cmd(cmd, err => { + if (err) { + reject(err); + } + + resolve(); + }); + }); + } +} + +module.exports.SevenZip = SevenZip; \ No newline at end of file diff --git a/utils/typeDef/STOW-RS/STOW-RS.d.ts b/utils/typeDef/STOW-RS/STOW-RS.d.ts new file mode 100644 index 00000000..6f75bb0c --- /dev/null +++ b/utils/typeDef/STOW-RS/STOW-RS.d.ts @@ -0,0 +1,35 @@ +import type { Fields, File } from "formidable"; +import type { GeneralDicomJson } from "../dicom"; + +type Multipart = { + fields?: Fields; + files?: File[]; +}; + +export type MultipartParseResult = { + status: boolean; + error?: string; + multipart: Multipart; +}; + +export type DicomFileSaveInfo = { + /** The path of saved file's directory */ + fullPath: string; + + /** The relative path of saved DICOM instance file. e.g. /files/123.dcm */ + relativePath: string; + + /** The full path of saved DICOM instance file. e.g. /home/app/files/123.dcm */ + instancePath: string; + + /** The relative path of series level directory */ + seriesPath: string; + + /** The relative path of study level directory */ + studyPath: string; + +}; + +export type SaveDicomFileResult = DicomFileSaveInfo & { + dicomJson: GeneralDicomJson; +}; diff --git a/utils/typeDef/WADO-RS/WADO-RS.def.js b/utils/typeDef/WADO-RS/WADO-RS.def.js deleted file mode 100644 index c8f897b1..00000000 --- a/utils/typeDef/WADO-RS/WADO-RS.def.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @typedef { Object } ImagePathObj - * @property { string } studyUID - * @property { string } seriesUID - * @property { string } instanceUID - * @property { string } instancePath - */ - -/** - * @typedef { { - * studyUID: string, - * seriesUID: string, - * instanceUID: string, - * instancePath: string, - * "00280008": string, - * "00020010": string - * } } InstanceFrameObj - */ - -module.exports.unUse = {}; \ No newline at end of file diff --git a/utils/typeDef/bulkdata.d.ts b/utils/typeDef/bulkdata.d.ts new file mode 100644 index 00000000..195c74a0 --- /dev/null +++ b/utils/typeDef/bulkdata.d.ts @@ -0,0 +1,7 @@ +export type BulkData = { + studyUID: string; + seriesUID: string; + instanceUID: string; + filename: string; + binaryValuePath: string; +}; \ No newline at end of file diff --git a/utils/typeDef/bulkdata.js b/utils/typeDef/bulkdata.js deleted file mode 100644 index b23b2f6f..00000000 --- a/utils/typeDef/bulkdata.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @typedef {object} BulkData - * @property {string} studyUID - * @property {string} seriesUID - * @property {string} instanceUID - * @property {string} filename - * @property {string} binaryValuePath The json path of the binary value - * - */ - -const BulkData = true; - -module.exports.unUse = {}; diff --git a/utils/typeDef/dicom.d.ts b/utils/typeDef/dicom.d.ts new file mode 100644 index 00000000..70bf1f20 --- /dev/null +++ b/utils/typeDef/dicom.d.ts @@ -0,0 +1,26 @@ +export type DicomUid = { + studyUID?: string; + seriesUID?: string; + instanceUID?: string; + sopClass?: string; + patientID: string; +}; + +export type DicomJsonQueryOptions = { + query: any; + limit?: number; + skip?: number; + retrieveBaseUrl?: string; + includeFields?: string[]; + requestParams?: any; + isRecycle?: boolean; +}; + +export type DicomJsonItem = { + vr: string; + Value: any[]; +}; + +export type GeneralDicomJson = { + [key: string]: DicomJsonItem; +} diff --git a/utils/typeDef/dicom.js b/utils/typeDef/dicom.js deleted file mode 100644 index f9aeb60d..00000000 --- a/utils/typeDef/dicom.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @typedef Uids - * @property {string} studyUID - * @property {string} [seriesUID] - * @property {string} [instanceUID] - */ - -/** - * @typedef {object} UIDObject - * @property {string} studyUID - * @property {string} seriesUID - * @property {string} sopInstanceUID - * @property {string} sopClass - * @property {string} patientID - * - */ - -/** - * @typedef DicomJsonMongoQueryOptions - * @property {object} query - * @property {number} limit - * @property {number} skip - * @property {string} retrieveBaseUrl - * @property {object} requestParams? - * @property {string[]} includeFields - */ - -const DICOM = true; - -module.exports.unUse = {}; diff --git a/utils/typeDef/dicomImage.d.ts b/utils/typeDef/dicomImage.d.ts new file mode 100644 index 00000000..7db267ca --- /dev/null +++ b/utils/typeDef/dicomImage.d.ts @@ -0,0 +1,19 @@ +import type { DicomJsonItem } from "./dicom"; + +export type ImagePathObj = { + studyUID: string; + seriesUID: string; + instanceUID: string; + instancePath: string; +}; + +export type InstanceFrameObj = { + studyUID: string; + seriesUID: string; + instanceUID: string; + instancePath: string; + "00280008": DicomJsonItem; + "00020010": DicomJsonItem; + "00281050": DicomJsonItem; + "00281051": DicomJsonItem; +}; diff --git a/utils/typeDef/sevenZip.d.ts b/utils/typeDef/sevenZip.d.ts new file mode 100644 index 00000000..2a2d7a55 --- /dev/null +++ b/utils/typeDef/sevenZip.d.ts @@ -0,0 +1,3 @@ +export type archiveType = "7z" | "zip" | "gzip" | "bzip2" | "tar"; + +export type overwriteMode = "a" | "s" | "u" | "t";