diff --git a/.env b/.env index 6a4c20293..778314434 100644 --- a/.env +++ b/.env @@ -25,7 +25,8 @@ VITE_CHRIS_UI_AUTH_URL="http://localhost:8000/api/v1/auth-token/" VITE_ALPHA_FEATURES='production' # Set PFDCM_URL to the root url of the running pfdcm instance -VITE_PFDCM_URL="http://localhost:4005/" +# note: must *not* have trailing slash +VITE_PFDCM_URL="http://localhost:4005" # (optional) set URL of an OHIF browser which has the same studies as the PFDCM # VITE_OHIF_URL="http://localhost:8042/ohif/" @@ -36,5 +37,8 @@ VITE_PFDCM_URL="http://localhost:4005/" VITE_SOURCEMAP='false' +# URI for support requests +VITE_SUPPORT_URL='mailto:dev@babyMRI.org' + # Set URL for the store if you want to see it in the sidebar VITE_CHRIS_STORE_URL= "https://cube.chrisproject.org/api/v1/" diff --git a/README.md b/README.md index 4b8179621..2d6228f65 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,21 @@ npm run test:e2e:local # run tests using "local" backend For more information, consult the wiki: https://github.com/FNNDSC/ChRIS_ui/wiki/E2E-Testing-with-Playwright +## Pfdcm Client + +The code in `src/api/pfdcm/generated` were automatically generated using the [OpenAPI generator](https://openapi-generator.tech). + +```shell +docker run --rm --net=host -u "$(id -u):$(id -g)" \ + -v "$(npm prefix)/src:/src" \ + docker.io/openapitools/openapi-generator-cli:v7.8.0 \ + generate -g typescript-fetch -i http://localhost:4005/openapi.json -o /src/api/pfdcm/generated +``` + +## Development Notes + +- Do not use `ReadonlyArray` because it is not supported by `antd` prop types. + [license-badge]: https://img.shields.io/github/license/fnndsc/chris_ui.svg diff --git a/biome.json b/biome.json index 667c21416..4e93c86e1 100644 --- a/biome.json +++ b/biome.json @@ -7,10 +7,11 @@ "package.json", "biome.json", "*.config.ts", + "vitest.*.ts", "tsconfig*.json", "testing/*.mjs" ], - "ignore": ["package-lock.json"] + "ignore": ["package-lock.json", "pfdcm/generated"] }, "organizeImports": { "enabled": true diff --git a/package.json b/package.json index 640a34055..f05986e79 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "chrisomatic": "testing/chrisomatic.sh", "get-dicoms": "./testing/getDicoms.mjs", "list-mrns": "./testing/miniChRIS/scripts/list_mrns.sh", + "upload-dicom": "./testing/miniChRIS/scripts/upload2orthanc.sh", "lint": "biome lint .", "fix": "biome check --apply .", "fix:unsafe": "biome check --apply-unsafe .", @@ -37,6 +38,7 @@ "print-version": "node printVersion.js" }, "dependencies": { + "@ant-design/icons": "^5.5.1", "@cornerstonejs/core": "^1.84.4", "@cornerstonejs/dicom-image-loader": "^1.84.4", "@cornerstonejs/streaming-image-volume-loader": "^1.84.4", @@ -89,6 +91,7 @@ "react-redux": "^9.1.2", "react-router": "^6.26.2", "react-router-dom": "^6.26.2", + "react-use-websocket": "^4.8.1", "redux-saga": "^1.3.0", "rusha": "^0.8.14", "sanitize-html": "^2.13.0", @@ -104,6 +107,9 @@ "@faker-js/faker": "^9.0.1", "@playwright/test": "^1.46.1", "@redux-devtools/extension": "^3.3.0", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", "@types/d3-hierarchy": "3.1.7", "@types/d3-selection": "3.0.10", "@types/d3-shape": "^3.1.6", @@ -123,8 +129,8 @@ "redux-logger": "4.0.0", "vite": "5.4.6", "vite-plugin-istanbul": "^6.0.2", - "vite-plugin-node-polyfills": "^0.22.0", - "vitest": "2.1.1" + "vitest": "^2.1.1", + "vitest-websocket-mock": "^0.4.0" }, "type": "module", "packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db6cd26d5..c5d08435b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@ant-design/icons': + specifier: ^5.5.1 + version: 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@cornerstonejs/core': specifier: ^1.84.4 version: 1.84.4(@babel/preset-env@7.25.4(@babel/core@7.25.2))(autoprefixer@10.4.20(postcss@8.4.47))(webpack@5.94.0)(wslink@2.2.1) @@ -164,6 +167,9 @@ importers: react-router-dom: specifier: ^6.26.2 version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-use-websocket: + specifier: ^4.8.1 + version: 4.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) redux-saga: specifier: ^1.3.0 version: 1.3.0 @@ -204,6 +210,15 @@ importers: '@redux-devtools/extension': specifier: ^3.3.0 version: 3.3.0(redux@5.0.1) + '@testing-library/dom': + specifier: ^10.4.0 + version: 10.4.0 + '@testing-library/jest-dom': + specifier: ^6.5.0 + version: 6.5.0 + '@testing-library/react': + specifier: ^16.0.1 + version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/d3-hierarchy': specifier: 3.1.7 version: 3.1.7 @@ -261,15 +276,18 @@ importers: vite-plugin-istanbul: specifier: ^6.0.2 version: 6.0.2(vite@5.4.6(@types/node@22.5.5)(terser@5.33.0)) - vite-plugin-node-polyfills: - specifier: ^0.22.0 - version: 0.22.0(rollup@4.21.1)(vite@5.4.6(@types/node@22.5.5)(terser@5.33.0)) vitest: - specifier: 2.1.1 + specifier: ^2.1.1 version: 2.1.1(@types/node@22.5.5)(happy-dom@15.0.0)(terser@5.33.0) + vitest-websocket-mock: + specifier: ^0.4.0 + version: 0.4.0(vitest@2.1.1(@types/node@22.5.5)(happy-dom@15.0.0)(terser@5.33.0)) packages: + '@adobe/css-tools@4.4.0': + resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -296,8 +314,8 @@ packages: '@ant-design/icons-svg@4.4.2': resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} - '@ant-design/icons@5.4.0': - resolution: {integrity: sha512-QZbWC5xQYexCI5q4/fehSEkchJr5UGtvAJweT743qKUQQGs9IH2DehNLP49DJ3Ii9m9CijD2HN6fNy3WKhIFdA==} + '@ant-design/icons@5.5.1': + resolution: {integrity: sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w==} engines: {node: '>=8'} peerDependencies: react: '>=16.0.0' @@ -1415,24 +1433,6 @@ packages: peerDependencies: react: '>=16.8.0' - '@rollup/plugin-inject@5.0.5': - resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/pluginutils@5.1.2': - resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - '@rollup/rollup-android-arm-eabi@4.21.1': resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==} cpu: [arm] @@ -1596,9 +1596,35 @@ packages: peerDependencies: react: ^18 || ^19 + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.5.0': + resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.0.1': + resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 + '@types/react-dom': ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1862,6 +1888,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1882,6 +1912,13 @@ packages: argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + array-equal@1.0.2: resolution: {integrity: sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==} @@ -1891,12 +1928,6 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - asn1.js@4.10.1: - resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} - - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1915,10 +1946,6 @@ packages: peerDependencies: postcss: ^8.1.0 - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - axios@1.7.5: resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==} @@ -1951,52 +1978,18 @@ packages: base16@1.0.0: resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - bezier-js@6.1.4: resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==} big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - bn.js@4.12.0: - resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - - bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - - browser-resolve@2.0.0: - resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} - - browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - - browserify-cipher@1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} - - browserify-des@1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} - - browserify-rsa@4.1.0: - resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} - - browserify-sign@4.2.3: - resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} - engines: {node: '>= 0.12'} - - browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - browserslist@4.23.3: resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2005,15 +1998,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -2022,10 +2006,6 @@ packages: resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} engines: {node: '>=8'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2052,6 +2032,14 @@ packages: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} @@ -2063,9 +2051,6 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - cipher-base@1.0.4: - resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} - classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -2116,12 +2101,6 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - - constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -2141,25 +2120,10 @@ packages: core-js@3.38.1: resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} - create-ecdh@4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} - - create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - - create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-fetch@3.1.8: resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} @@ -2167,8 +2131,8 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - crypto-browserify@3.12.0: - resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} cssfilter@0.0.10: resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} @@ -2339,9 +2303,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - des.js@1.1.0: - resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} - detect-gpu@5.0.45: resolution: {integrity: sha512-59oJS71ODAVPGMjBfyHPPOnM9efOhJpOliQZ8I1UG4p440PjYCVYWgAgY+BiO3JPe/BXbRzPAn+XbVqIESXkDQ==} @@ -2351,8 +2312,11 @@ packages: dicom-parser@1.8.21: resolution: {integrity: sha512-lYCweHQDsC8UFpXErPlg86Px2A8bay0HiUY+wzoG3xv5GzgqVHU3lziwSc/Gzn7VV7y2KeP072SzCviuOoU02w==} - diffie-hellman@5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -2360,10 +2324,6 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - domain-browser@4.23.0: - resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} - engines: {node: '>=10'} - domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -2380,9 +2340,6 @@ packages: electron-to-chromium@1.5.13: resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} - elliptic@6.5.7: - resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} - email-validator@2.0.4: resolution: {integrity: sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==} engines: {node: '>4.0'} @@ -2468,9 +2425,6 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -2485,9 +2439,6 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2528,10 +2479,6 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - flux@4.0.4: resolution: {integrity: sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==} peerDependencies: @@ -2549,9 +2496,6 @@ packages: debug: optional: true - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - force-graph@1.43.5: resolution: {integrity: sha512-HveLELh9yhZXO/QOfaFS38vlwJZ/3sKu+jarfXzRmbmihSOH/BbRWnUvmg8wLFiYy6h4HlH4lkRfZRccHYmXgA==} engines: {node: '>=12'} @@ -2667,21 +2611,6 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hash-base@3.0.4: - resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} - engines: {node: '>=4'} - - hash-base@3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} - - hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - hasha@5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} @@ -2690,9 +2619,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -2702,12 +2628,6 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - https-browserify@1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - immer@10.1.1: resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} @@ -2752,17 +2672,9 @@ packages: invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - is-core-module@2.15.1: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} @@ -2771,14 +2683,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -2787,10 +2691,6 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -2798,16 +2698,9 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-timers-promises@1.0.1: - resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} - engines: {node: '>=10'} - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -2972,10 +2865,6 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} @@ -3013,6 +2902,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} @@ -3032,9 +2925,6 @@ packages: engines: {node: '>= 18'} hasBin: true - md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} @@ -3122,10 +3012,6 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} - miller-rabin@4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -3134,11 +3020,9 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3157,6 +3041,10 @@ packages: mitt@1.2.0: resolution: {integrity: sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==} + mock-socket@9.3.1: + resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==} + engines: {node: '>= 8'} + ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -3198,10 +3086,6 @@ packages: node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - node-stdlib-browser@1.2.1: - resolution: {integrity: sha512-dZezG3D88Lg22DwyjsDuUs7cCT/XGr8WwJgg/S3ZnkcWuPet2Tt/W1d2Eytb1Z73JpZv+XVCDI5TWv6UMRq0Gg==} - engines: {node: '>=10'} - normalize-range@0.1.2: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} @@ -3215,44 +3099,21 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} - - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - p-map@3.0.0: resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} engines: {node: '>=8'} @@ -3286,10 +3147,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-asn1@5.1.7: - resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} - engines: {node: '>= 0.10'} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -3297,9 +3154,6 @@ packages: parse-srcset@1.0.2: resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3330,28 +3184,16 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} - pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} - picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} picocolors@1.1.0: resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - pkg-dir@5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} - platform@1.3.6: resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} @@ -3365,10 +3207,6 @@ packages: engines: {node: '>=18'} hasBin: true - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -3380,21 +3218,18 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + preval.macro@5.0.0: resolution: {integrity: sha512-+OZRqZYx1pjZ7H5Jis8bPFXkiT7lwA46UzAT4IjuzFVKwkJK+TwIx1TCqrqNCf8U3e5O12mEJEz1BXslkCLWfQ==} engines: {node: '>=10'} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - process-on-spawn@1.0.0: resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==} engines: {node: '>=8'} - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} @@ -3404,12 +3239,6 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - public-encrypt@4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} - - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3417,24 +3246,13 @@ packages: pure-color@1.3.0: resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} - query-string@9.1.0: resolution: {integrity: sha512-t6dqMECpCkqfyv2FfwVS1xcB6lgXW/0XZSaKdsCNGYkqMO76AFiJEg4vINzoDKcZa6MS7JX+OHIjwh06K5vczw==} engines: {node: '>=18'} - querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - randomfill@1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - rc-cascader@3.28.1: resolution: {integrity: sha512-9+8oHIMWVLHxuaapDiqFNmD9KSyKN/P4bo9x/MBuDbyTqP8f2/POmmZxdXWBO3yq/uE3pKyQCXYNUxrNfHRv2A==} peerDependencies: @@ -3719,6 +3537,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3790,6 +3611,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-use-websocket@4.8.1: + resolution: {integrity: sha512-FTXuG5O+LFozmu1BRfrzl7UIQngECvGJmL7BHsK4TYXuVt+mCizVA8lT0hGSIF0Z0TedF7bOo1nRzOUdginhDw==} + peerDependencies: + react: '>= 18.0.0' + react-dom: '>= 18.0.0' + react-virtualized@9.22.5: resolution: {integrity: sha512-YqQMRzlVANBv1L/7r63OHa2b0ZsAaDp1UhVNEdUaXI8A5u6hTpA5NYtUueLH2rFuY/27mTGIBl7ZhqFKzw18YQ==} peerDependencies: @@ -3800,9 +3627,6 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3811,6 +3635,10 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + redux-logger@4.0.0: resolution: {integrity: sha512-dl+5mQjk70HIlrgOgPMAL0d0hOhBTPQcG5zPPlPZKa/Yf4lU6A37mv3Xqn3lFp0eUguSApIa2GD/YJVOIQQi5A==} @@ -3884,9 +3712,6 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - rollup@4.21.1: resolution: {integrity: sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -3935,17 +3760,9 @@ packages: set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - shader-loader@1.3.1: resolution: {integrity: sha512-dt8F9K0x4rjmaFyHh7rNDfpt4LUiR64zhNIEwp2WbE99B3z4ALuvvmhftkElg93dUD6sTmv/aXa/z9SJiEddcA==} @@ -3965,10 +3782,6 @@ packages: shlex@2.1.2: resolution: {integrity: sha512-Nz6gtibMVgYeMEhUjp2KuwAgqaJA1K155dU/HuDaEJUGgnmYfVtVZah+uerVWdH8UGnyahhDCgABbYTbs254+w==} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -4021,9 +3834,6 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - stream-http@3.2.0: - resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} - string-convert@0.2.1: resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} @@ -4050,6 +3860,10 @@ packages: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + stylis@4.3.4: resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} @@ -4112,10 +3926,6 @@ packages: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} engines: {node: '>=12.22'} - timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -4150,9 +3960,6 @@ packages: tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - tty-browserify@0.0.1: - resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -4213,10 +4020,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url@0.11.4: - resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} - engines: {node: '>= 0.4'} - use-ackee@3.1.0: resolution: {integrity: sha512-RPX51I/Ak6dtNW0aXY6GM/LJ899OU4qoKes/CTGj9+4Qoz9PO7I14BX8Rw6vmlbVvQzZ6WIb01sg7vU/kYEQOw==} engines: {node: '>= 14'} @@ -4260,9 +4063,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true @@ -4399,11 +4199,6 @@ packages: peerDependencies: vite: '>=4 <=6' - vite-plugin-node-polyfills@0.22.0: - resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} - peerDependencies: - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - vite@5.4.6: resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4435,6 +4230,11 @@ packages: terser: optional: true + vitest-websocket-mock@0.4.0: + resolution: {integrity: sha512-tGnOwE2nC8jfioQXDrX+lZ8EVrF+IO2NVqe1vV9h945W/hlR0S6ZYbMqCJGG3Nyd//c5XSe1IGLD2ZgE2D1I7Q==} + peerDependencies: + vitest: '>=2' + vitest@2.1.1: resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4460,9 +4260,6 @@ packages: jsdom: optional: true - vm-browserify@1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} @@ -4510,10 +4307,6 @@ packages: which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4560,10 +4353,6 @@ packages: engines: {node: '>= 0.10.0'} hasBin: true - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -4582,12 +4371,10 @@ packages: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - snapshots: + '@adobe/css-tools@4.4.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -4623,11 +4410,11 @@ snapshots: '@ant-design/icons-svg@4.4.2': {} - '@ant-design/icons@5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ant-design/icons@5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ant-design/colors': 7.1.0 '@ant-design/icons-svg': 4.4.2 - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 classnames: 2.5.1 rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 @@ -5963,22 +5750,6 @@ snapshots: dequal: 2.0.3 react: 18.3.1 - '@rollup/plugin-inject@5.0.5(rollup@4.21.1)': - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.21.1) - estree-walker: 2.0.2 - magic-string: 0.30.11 - optionalDependencies: - rollup: 4.21.1 - - '@rollup/pluginutils@5.1.2(rollup@4.21.1)': - dependencies: - '@types/estree': 1.0.6 - estree-walker: 2.0.2 - picomatch: 2.3.1 - optionalDependencies: - rollup: 4.21.1 - '@rollup/rollup-android-arm-eabi@4.21.1': optional: true @@ -6086,8 +5857,41 @@ snapshots: '@tanstack/query-core': 5.56.2 react: 18.3.1 + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/runtime': 7.25.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.5.0': + dependencies: + '@adobe/css-tools': 4.4.0 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + + '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@testing-library/dom': 10.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + '@tweenjs/tween.js@23.1.3': {} + '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.4 @@ -6393,6 +6197,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} antd@5.20.6(date-fns@4.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -6400,7 +6206,7 @@ snapshots: '@ant-design/colors': 7.1.0 '@ant-design/cssinjs': 1.21.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ant-design/cssinjs-utils': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ant-design/icons': 5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/icons': 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ant-design/react-slick': 1.1.2(react@18.3.1) '@babel/runtime': 7.25.4 '@ctrl/tinycolor': 3.6.1 @@ -6463,26 +6269,18 @@ snapshots: dependencies: sprintf-js: 1.0.3 + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + array-equal@1.0.2: {} array-tree-filter@2.1.0: {} asap@2.0.6: {} - asn1.js@4.10.1: - dependencies: - bn.js: 4.12.0 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - assert@2.1.0: - dependencies: - call-bind: 1.0.7 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.5 - util: 0.12.5 - assertion-error@2.0.1: {} asynckit@0.4.0: {} @@ -6499,10 +6297,6 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 - axios@1.7.5: dependencies: follow-redirects: 1.15.6 @@ -6552,16 +6346,10 @@ snapshots: base16@1.0.0: {} - base64-js@1.5.1: {} - bezier-js@6.1.4: {} big.js@5.2.2: {} - bn.js@4.12.0: {} - - bn.js@5.2.1: {} - brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -6571,56 +6359,6 @@ snapshots: dependencies: balanced-match: 1.0.2 - brorand@1.1.0: {} - - browser-resolve@2.0.0: - dependencies: - resolve: 1.22.8 - - browserify-aes@1.2.0: - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.4 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-cipher@1.0.1: - dependencies: - browserify-aes: 1.2.0 - browserify-des: 1.0.2 - evp_bytestokey: 1.0.3 - - browserify-des@1.0.2: - dependencies: - cipher-base: 1.0.4 - des.js: 1.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-rsa@4.1.0: - dependencies: - bn.js: 5.2.1 - randombytes: 2.1.0 - - browserify-sign@4.2.3: - dependencies: - bn.js: 5.2.1 - browserify-rsa: 4.1.0 - create-hash: 1.2.0 - create-hmac: 1.1.7 - elliptic: 6.5.7 - hash-base: 3.0.4 - inherits: 2.0.4 - parse-asn1: 5.1.7 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - - browserify-zlib@0.2.0: - dependencies: - pako: 1.0.11 - browserslist@4.23.3: dependencies: caniuse-lite: 1.0.30001653 @@ -6630,15 +6368,6 @@ snapshots: buffer-from@1.1.2: {} - buffer-xor@1.0.3: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - builtin-status-codes@3.0.0: {} - cac@6.7.14: {} caching-transform@4.0.0: @@ -6648,14 +6377,6 @@ snapshots: package-hash: 4.0.0 write-file-atomic: 3.0.3 - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - callsites@3.1.0: {} camelcase@5.3.1: {} @@ -6682,17 +6403,22 @@ snapshots: escape-string-regexp: 1.0.5 supports-color: 5.5.0 + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + character-entities@2.0.2: {} check-error@2.1.1: {} chrome-trace-event@1.0.4: {} - cipher-base@1.0.4: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - classnames@2.5.1: {} clean-stack@2.2.0: {} @@ -6733,10 +6459,6 @@ snapshots: concat-map@0.0.1: {} - console-browserify@1.2.0: {} - - constants-browserify@1.0.0: {} - convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} @@ -6753,8 +6475,6 @@ snapshots: core-js@3.38.1: {} - core-util-is@1.0.3: {} - cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -6763,30 +6483,6 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 - create-ecdh@4.0.4: - dependencies: - bn.js: 4.12.0 - elliptic: 6.5.7 - - create-hash@1.2.0: - dependencies: - cipher-base: 1.0.4 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.2 - sha.js: 2.4.11 - - create-hmac@1.1.7: - dependencies: - cipher-base: 1.0.4 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - - create-require@1.1.1: {} - cross-fetch@3.1.8: dependencies: node-fetch: 2.7.0 @@ -6799,19 +6495,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crypto-browserify@3.12.0: - dependencies: - browserify-cipher: 1.0.1 - browserify-sign: 4.2.3 - create-ecdh: 4.0.4 - create-hash: 1.2.0 - create-hmac: 1.1.7 - diffie-hellman: 5.0.3 - inherits: 2.0.4 - pbkdf2: 3.1.2 - public-encrypt: 4.0.3 - randombytes: 2.1.0 - randomfill: 1.0.4 + css.escape@1.5.1: {} cssfilter@0.0.10: {} @@ -6962,11 +6646,6 @@ snapshots: dequal@2.0.3: {} - des.js@1.1.0: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - detect-gpu@5.0.45: dependencies: webgl-constants: 1.1.1 @@ -6977,11 +6656,9 @@ snapshots: dicom-parser@1.8.21: {} - diffie-hellman@5.0.3: - dependencies: - bn.js: 4.12.0 - miller-rabin: 4.0.1 - randombytes: 2.1.0 + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} dom-helpers@5.2.1: dependencies: @@ -6994,8 +6671,6 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 - domain-browser@4.23.0: {} - domelementtype@2.3.0: {} domhandler@5.0.3: @@ -7012,16 +6687,6 @@ snapshots: electron-to-chromium@1.5.13: {} - elliptic@6.5.7: - dependencies: - bn.js: 4.12.0 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - email-validator@2.0.4: {} emoji-regex@8.0.0: {} @@ -7106,11 +6771,9 @@ snapshots: estraverse@5.3.0: {} - estree-walker@2.0.2: {} - estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 esutils@2.0.3: {} @@ -7118,11 +6781,6 @@ snapshots: events@3.3.0: {} - evp_bytestokey@1.0.3: - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 - fast-deep-equal@3.1.3: {} fast-json-stable-stringify@2.1.0: {} @@ -7170,11 +6828,6 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - flux@4.0.4(react@18.3.1): dependencies: fbemitter: 3.0.0 @@ -7189,10 +6842,6 @@ snapshots: follow-redirects@1.15.6: {} - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - force-graph@1.43.5: dependencies: '@tweenjs/tween.js': 23.1.3 @@ -7312,26 +6961,6 @@ snapshots: has-symbols@1.0.3: {} - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 - - hash-base@3.0.4: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - hash-base@3.1.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - safe-buffer: 5.2.1 - - hash.js@1.1.7: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - hasha@5.2.2: dependencies: is-stream: 2.0.1 @@ -7341,12 +6970,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hmac-drbg@1.0.1: - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -7360,10 +6983,6 @@ snapshots: domutils: 3.1.0 entities: 4.5.0 - https-browserify@1.0.0: {} - - ieee754@1.2.1: {} - immer@10.1.1: {} immutable@3.8.2: {} @@ -7396,48 +7015,24 @@ snapshots: dependencies: loose-envify: 1.4.0 - is-arguments@1.1.1: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - is-arrayish@0.2.1: {} - is-callable@1.2.7: {} - is-core-module@2.15.1: dependencies: hasown: 2.0.2 is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.0.10: - dependencies: - has-tostringtag: 1.0.2 - - is-nan@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - is-plain-object@5.0.0: {} is-stream@2.0.1: {} - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - is-typedarray@1.0.0: {} is-windows@1.0.2: {} - isarray@1.0.0: {} - isexe@2.0.0: {} - isomorphic-timers-promises@1.0.1: {} - istanbul-lib-coverage@3.2.2: {} istanbul-lib-hook@3.0.0: @@ -7605,10 +7200,6 @@ snapshots: dependencies: p-locate: 4.1.0 - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - lodash-es@4.17.21: {} lodash.clonedeep@4.5.0: {} @@ -7639,6 +7230,8 @@ snapshots: dependencies: yallist: 3.1.1 + lz-string@1.5.0: {} + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -7659,12 +7252,6 @@ snapshots: marked@14.1.2: {} - md5.js@1.3.5: - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - memoize-one@5.2.1: {} merge-stream@2.0.0: {} @@ -7853,20 +7440,13 @@ snapshots: transitivePeerDependencies: - supports-color - miller-rabin@4.0.1: - dependencies: - bn.js: 4.12.0 - brorand: 1.1.0 - mime-db@1.52.0: {} mime-types@2.1.35: dependencies: mime-db: 1.52.0 - minimalistic-assert@1.0.1: {} - - minimalistic-crypto-utils@1.0.1: {} + min-indent@1.0.1: {} minimatch@3.1.2: dependencies: @@ -7882,6 +7462,8 @@ snapshots: mitt@1.2.0: {} + mock-socket@9.3.1: {} + ms@2.1.2: {} ms@2.1.3: {} @@ -7911,36 +7493,6 @@ snapshots: node-releases@2.0.18: {} - node-stdlib-browser@1.2.1: - dependencies: - assert: 2.1.0 - browser-resolve: 2.0.0 - browserify-zlib: 0.2.0 - buffer: 5.7.1 - console-browserify: 1.2.0 - constants-browserify: 1.0.0 - create-require: 1.1.1 - crypto-browserify: 3.12.0 - domain-browser: 4.23.0 - events: 3.3.0 - https-browserify: 1.0.0 - isomorphic-timers-promises: 1.0.1 - os-browserify: 0.3.0 - path-browserify: 1.0.1 - pkg-dir: 5.0.0 - process: 0.11.10 - punycode: 1.4.1 - querystring-es3: 0.2.1 - readable-stream: 3.6.2 - stream-browserify: 3.0.0 - stream-http: 3.2.0 - string_decoder: 1.1.1 - timers-browserify: 2.0.12 - tty-browserify: 0.0.1 - url: 0.11.4 - util: 0.12.5 - vm-browserify: 1.1.2 - normalize-range@0.1.2: {} nyc@17.0.0: @@ -7977,44 +7529,20 @@ snapshots: object-assign@4.1.1: {} - object-inspect@1.13.2: {} - - object-is@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - object-keys@1.1.1: {} - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 - os-browserify@0.3.0: {} - p-limit@2.3.0: dependencies: p-try: 2.2.0 - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - p-map@3.0.0: dependencies: aggregate-error: 3.1.0 @@ -8045,15 +7573,6 @@ snapshots: dependencies: callsites: 3.1.0 - parse-asn1@5.1.7: - dependencies: - asn1.js: 4.10.1 - browserify-aes: 1.2.0 - evp_bytestokey: 1.0.3 - hash-base: 3.0.4 - pbkdf2: 3.1.2 - safe-buffer: 5.2.1 - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.24.7 @@ -8063,8 +7582,6 @@ snapshots: parse-srcset@1.0.2: {} - path-browserify@1.0.1: {} - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -8084,28 +7601,14 @@ snapshots: pathval@2.0.0: {} - pbkdf2@3.1.2: - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - picocolors@1.0.1: {} picocolors@1.1.0: {} - picomatch@2.3.1: {} - pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - pkg-dir@5.0.0: - dependencies: - find-up: 5.0.0 - platform@1.3.6: {} playwright-core@1.46.1: {} @@ -8116,8 +7619,6 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - possible-typed-array-names@1.0.0: {} - postcss-value-parser@4.2.0: {} postcss@8.4.41: @@ -8132,18 +7633,20 @@ snapshots: picocolors: 1.1.0 source-map-js: 1.2.1 + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + preval.macro@5.0.0: dependencies: babel-plugin-preval: 5.1.0 - process-nextick-args@2.0.1: {} - process-on-spawn@1.0.0: dependencies: fromentries: 1.3.2 - process@0.11.10: {} - promise@7.3.1: dependencies: asap: 2.0.6 @@ -8156,42 +7659,20 @@ snapshots: proxy-from-env@1.1.0: {} - public-encrypt@4.0.3: - dependencies: - bn.js: 4.12.0 - browserify-rsa: 4.1.0 - create-hash: 1.2.0 - parse-asn1: 5.1.7 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - punycode@1.4.1: {} - punycode@2.3.1: {} pure-color@1.3.0: {} - qs@6.13.0: - dependencies: - side-channel: 1.0.6 - query-string@9.1.0: dependencies: decode-uri-component: 0.4.1 filter-obj: 5.1.0 split-on-first: 3.0.0 - querystring-es3@0.2.1: {} - randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - randomfill@1.0.4: - dependencies: - randombytes: 2.1.0 - safe-buffer: 5.2.1 - rc-cascader@3.28.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.4 @@ -8587,6 +8068,8 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-is@18.3.1: {} react-json-view@1.21.3(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -8679,6 +8162,11 @@ snapshots: transitivePeerDependencies: - '@types/react' + react-use-websocket@4.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-virtualized@9.22.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.4 @@ -8694,16 +8182,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -8714,6 +8192,11 @@ snapshots: dependencies: resolve: 1.22.8 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + redux-logger@4.0.0: dependencies: deep-diff: 0.3.8 @@ -8781,11 +8264,6 @@ snapshots: dependencies: glob: 7.2.3 - ripemd160@2.0.2: - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - rollup@4.21.1: dependencies: '@types/estree': 1.0.5 @@ -8853,22 +8331,8 @@ snapshots: set-blocking@2.0.0: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - setimmediate@1.0.5: {} - sha.js@2.4.11: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - shader-loader@1.3.1: dependencies: loader-utils: 1.4.2 @@ -8887,13 +8351,6 @@ snapshots: shlex@2.1.2: {} - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.2 - siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -8937,13 +8394,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - stream-http@3.2.0: - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - xtend: 4.0.2 - string-convert@0.2.1: {} string-width@4.2.3: @@ -8972,6 +8422,10 @@ snapshots: strip-bom@4.0.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + stylis@4.3.4: {} supports-color@5.5.0: @@ -9024,10 +8478,6 @@ snapshots: throttle-debounce@5.0.2: {} - timers-browserify@2.0.12: - dependencies: - setimmediate: 1.0.5 - tinybench@2.9.0: {} tinycolor2@1.6.0: {} @@ -9048,8 +8498,6 @@ snapshots: tslib@2.7.0: {} - tty-browserify@0.0.1: {} - type-fest@0.8.1: {} typedarray-to-buffer@3.1.5: @@ -9106,11 +8554,6 @@ snapshots: dependencies: punycode: 2.3.1 - url@0.11.4: - dependencies: - punycode: 1.4.1 - qs: 6.13.0 - use-ackee@3.1.0(react@18.3.1): dependencies: ackee-tracker: 5.1.0 @@ -9144,14 +8587,6 @@ snapshots: util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.1.1 - is-generator-function: 1.0.10 - is-typed-array: 1.1.13 - which-typed-array: 1.1.15 - uuid@10.0.0: {} uuid@8.3.2: {} @@ -9329,7 +8764,7 @@ snapshots: vite-node@2.1.1(@types/node@22.5.5)(terser@5.33.0): dependencies: cac: 6.7.14 - debug: 4.3.6 + debug: 4.3.7 pathe: 1.1.2 vite: 5.4.6(@types/node@22.5.5)(terser@5.33.0) transitivePeerDependencies: @@ -9366,14 +8801,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-node-polyfills@0.22.0(rollup@4.21.1)(vite@5.4.6(@types/node@22.5.5)(terser@5.33.0)): - dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.21.1) - node-stdlib-browser: 1.2.1 - vite: 5.4.6(@types/node@22.5.5)(terser@5.33.0) - transitivePeerDependencies: - - rollup - vite@5.4.6(@types/node@22.5.5)(terser@5.33.0): dependencies: esbuild: 0.21.5 @@ -9384,6 +8811,12 @@ snapshots: fsevents: 2.3.3 terser: 5.33.0 + vitest-websocket-mock@0.4.0(vitest@2.1.1(@types/node@22.5.5)(happy-dom@15.0.0)(terser@5.33.0)): + dependencies: + '@vitest/utils': 2.1.1 + mock-socket: 9.3.1 + vitest: 2.1.1(@types/node@22.5.5)(happy-dom@15.0.0)(terser@5.33.0) + vitest@2.1.1(@types/node@22.5.5)(happy-dom@15.0.0)(terser@5.33.0): dependencies: '@vitest/expect': 2.1.1 @@ -9394,7 +8827,7 @@ snapshots: '@vitest/spy': 2.1.1 '@vitest/utils': 2.1.1 chai: 5.1.1 - debug: 4.3.6 + debug: 4.3.7 magic-string: 0.30.11 pathe: 1.1.2 std-env: 3.7.0 @@ -9419,8 +8852,6 @@ snapshots: - supports-color - terser - vm-browserify@1.1.2: {} - warning@4.0.3: dependencies: loose-envify: 1.4.0 @@ -9481,14 +8912,6 @@ snapshots: which-module@2.0.1: {} - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -9548,8 +8971,6 @@ snapshots: commander: 2.20.3 cssfilter: 0.0.10 - xtend@4.0.2: {} - y18n@4.0.3: {} yallist@3.1.1: {} @@ -9574,5 +8995,3 @@ snapshots: which-module: 2.0.1 y18n: 4.0.3 yargs-parser: 18.1.3 - - yocto-queue@0.1.0: {} diff --git a/src/App.tsx b/src/App.tsx index d5105ba54..f316263e5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import "@patternfly/react-core/dist/styles/base.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { ConfigProvider, theme } from "antd"; +import { ConfigProvider, App as AntdApp, theme } from "antd"; import { useContext } from "react"; import { CookiesProvider } from "react-cookie"; import { Provider } from "react-redux"; @@ -62,8 +62,12 @@ function App(props: AllProps) { : theme.defaultAlgorithm, }} > - - + +
+ + +
+
diff --git a/src/api/chrisapiclient.ts b/src/api/chrisapiclient.ts index 16ba44771..5b062baca 100644 --- a/src/api/chrisapiclient.ts +++ b/src/api/chrisapiclient.ts @@ -7,7 +7,7 @@ import { Cookies } from "react-cookie"; * passed the token, declare process.env variables, etc. */ -// biome-ignore lint/complexity/noStaticOnlyClass: +// biome-ignore lint/complexity/noStaticOnlyClass: Singleton pattern class ChrisAPIClient { private static client: Client; private static isTokenAuthorized: boolean; diff --git a/src/api/fp/chrisapi.ts b/src/api/fp/chrisapi.ts index df3ad7fa0..4ab2c7238 100644 --- a/src/api/fp/chrisapi.ts +++ b/src/api/fp/chrisapi.ts @@ -1,17 +1,14 @@ import Client, { AllPluginInstanceList, + DownloadToken, Feed, FeedPluginInstanceList, - FileBrowserPath, - FileBrowserPathFileList, PluginInstance, PublicFeedList, } from "@fnndsc/chrisapi"; import * as TE from "fp-ts/TaskEither"; import * as E from "fp-ts/Either"; -import * as Console from "fp-ts/Console"; import { pipe } from "fp-ts/function"; -import FpFileBrowserFile from "./fpFileBrowserFile"; /** * fp-ts friendly wrapper for @fnndsc/chrisapi @@ -27,7 +24,7 @@ class FpClient { ...params: Parameters ): TE.TaskEither { return TE.tryCatch( - () => this.client.getPluginInstance(...params), + () => this.client.getPluginInstance(...params).then(notNull), E.toError, ); } @@ -48,6 +45,7 @@ class FpClient { }, }; if (this.client.auth.token) { + // @ts-ignore options.headers.Authorization = `Token ${this.client.auth.token}`; } return pipe( @@ -115,55 +113,22 @@ class FpClient { return TE.tryCatch(() => feed.getPluginInstances(...params), E.toError); } - /** - * A wrapper which calles `getFileBrowserPath` then `getFiles`, - * and processes the returned objects to have a more sane type. - * - * Pretty much gives you back what CUBE would return from - * `api/v1/filebrowser-files/.../` with HTTP header `Accept: application/json` - * - * Pagination is not implemented, hence the name "get **few** files under"... - */ - public getFewFilesUnder( - ...args: Parameters - ): TE.TaskEither> { - return pipe( - this.getFileBrowserPath(...args), - TE.flatMap((fb) => FpClient.filebrowserGetFiles(fb, { limit: 100 })), - TE.tapIO((list) => { - if (list.hasNextPage) { - return Console.warn( - `Not all elements from ${list.url} were fetched, ` + - "and pagination not implemented.", - ); - } - return () => undefined; - }), - TE.map(saneReturnOfFileBrowserPathFileList), - ); - } - - public getFileBrowserPath( - ...args: Parameters - ): TE.TaskEither { + public createDownloadToken( + ...params: Parameters + ): TE.TaskEither { return TE.tryCatch( - () => this.client.getFileBrowserPath(...args), + () => this.client.createDownloadToken(...params).then(notNull), E.toError, ); } - - public static filebrowserGetFiles( - fbp: FileBrowserPath, - ...params: Parameters - ): TE.TaskEither { - return TE.tryCatch(() => fbp.getFiles(...params), E.toError); - } } -function saneReturnOfFileBrowserPathFileList( - fbpfl: FileBrowserPathFileList, -): ReadonlyArray { - return fbpfl.getItems()!.map((file) => new FpFileBrowserFile(file)); +function notNull(x: T | null): T { + if (x === null) { + throw Error(); + } + return x; } -export { FpClient, saneReturnOfFileBrowserPathFileList, FpFileBrowserFile }; +export default FpClient; +export { FpClient }; diff --git a/src/api/fp/fpFileBrowserFile.ts b/src/api/fp/fpFileBrowserFile.ts deleted file mode 100644 index 856517bcd..000000000 --- a/src/api/fp/fpFileBrowserFile.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Collection, FileBrowserPathFile } from "@fnndsc/chrisapi"; -import * as TE from "fp-ts/TaskEither"; -import * as E from "fp-ts/Either"; -import { pipe } from "fp-ts/function"; - -/** - * A type-safe wrapper for `FileBrowserPathFile` which also handles - * the annoyances of Collection+JSON. - */ -class FpFileBrowserFile { - private readonly file: FileBrowserPathFile; - - constructor(file: FileBrowserPathFile) { - this.file = file; - } - - get creation_date(): string { - return this.file.data.creation_date; - } - - get fname(): string { - return this.file.data.fname; - } - - get fsize(): number { - return this.file.data.fsize; - } - - get file_resource() { - return Collection.getLinkRelationUrls( - this.file.collection.items[0], - "file_resource", - )[0]; - } - - get url() { - return Collection.getLinkRelationUrls( - this.file.collection.items[0], - "url", - )[0]; - } - - getFileBlob( - ...args: Parameters - ): TE.TaskEither { - return TE.tryCatch(() => this.file.getFileBlob(...args), E.toError); - } - - /** - * Get the data of this file, assuming it is UTF-8 plaintext. - */ - getAsText( - ...args: Parameters - ): TE.TaskEither { - return pipe( - this.getFileBlob(...args), - TE.flatMap((blob) => { - const task = () => blob.text(); - return TE.rightTask(task); - }), - ); - } -} - -export default FpFileBrowserFile; diff --git a/src/api/lonk/LonkSubscriber.ts b/src/api/lonk/LonkSubscriber.ts new file mode 100644 index 000000000..ee6df51a7 --- /dev/null +++ b/src/api/lonk/LonkSubscriber.ts @@ -0,0 +1,165 @@ +import { + Lonk, + LonkDone, + LonkError, + LonkHandlers, + LonkProgress, + LonkSubscription, + LonkUnsubscription, + SeriesKey, +} from "./types.ts"; +import deserialize from "./de.ts"; +import { pipe, identity } from "fp-ts/function"; +import * as E from "fp-ts/Either"; +import SeriesMap from "./seriesMap.ts"; +import useWebSocket from "react-use-websocket"; + +/** + * `LonkSubscriber` wraps a {@link WebSocket}, routing incoming JSON messages + * to corresponding functions of {@link LonkHandlers}. + */ +class LonkSubscriber { + private readonly pendingSubscriptions: SeriesMap SeriesKey)>; + private readonly pendingUnsubscriptions: (() => void)[]; + private readonly handlers: LonkHandlers; + + public constructor(handlers: LonkHandlers) { + this.handlers = handlers; + this.pendingSubscriptions = new SeriesMap(); + this.pendingUnsubscriptions = []; + } + + /** + * Handle an incoming message, calling + */ + public handle(data: any) { + pipe( + data, + deserialize, + E.flatMap((data) => this.routeMessage(data)), + E.match( + (e) => + this.handlers.onMessageError && this.handlers.onMessageError(data, e), + identity, + ), + ); + } + + private routeMessage( + data: E.Either>, + ): E.Either { + return pipe( + data, + E.match( + () => this.handleUnsubscription(), + (lonk) => this.routeLonk(lonk), + ), + ); + } + + private routeLonk(data: Lonk): E.Either { + const { onProgress, onDone, onError } = this.handlers; + const { SeriesInstanceUID, pacs_name, message } = data; + // note: for performance reasons, this if-else chain is in + // descending order of case frequency. + if (isProgress(message)) { + onProgress(pacs_name, SeriesInstanceUID, message.ndicom); + } else if (isDone(message)) { + onDone(pacs_name, SeriesInstanceUID); + } else if (isSubscribed(message)) { + this.handleSubscriptionSuccess(pacs_name, SeriesInstanceUID); + } else if (isError(message)) { + onError(pacs_name, SeriesInstanceUID, message.error); + } else { + return E.left(`Unrecognized message: ${JSON.stringify(message)}`); + } + return E.right(null); + } + + private handleSubscriptionSuccess( + pacs_name: string, + SeriesInstanceUID: string, + ) { + const callback = this.pendingSubscriptions.pop( + pacs_name, + SeriesInstanceUID, + ); + if (callback) { + callback(); + } else { + throw new Error( + "Got subscription response, but never requested subscription. " + + `pacs_name=${pacs_name} SeriesInstanceUID=${SeriesInstanceUID}`, + ); + } + } + + private handleUnsubscription(): E.Either { + const callback = this.pendingUnsubscriptions.pop(); + if (callback) { + callback(); + return E.right(null); + } else { + return E.left( + "Got unsubscription response, but never requested unsubscription", + ); + } + } + + /** + * Subscribe to notifications for a series. + */ + public subscribe( + pacs_name: string, + SeriesInstanceUID: string, + { sendJsonMessage }: ReturnType, + ): Promise { + let callback = null; + const promise: Promise = new Promise((resolve) => { + callback = () => resolve({ SeriesInstanceUID, pacs_name }); + }); + this.pendingSubscriptions.set(pacs_name, SeriesInstanceUID, callback); + sendJsonMessage({ + SeriesInstanceUID, + pacs_name, + action: "subscribe", + }); + return promise; + } + + /** + * Unsubscribe from notifications for all series. + * + * https://chrisproject.org/docs/oxidicom/lonk-ws#unsubscribe + */ + public unsubscribeAll({ + sendJsonMessage, + }: ReturnType): Promise { + let callback = null; + const promise: Promise = new Promise((resolve) => { + callback = resolve; + }); + callback && this.pendingUnsubscriptions.push(callback); + sendJsonMessage({ action: "unsubscribe" }); + return promise; + } +} + +function isSubscribed(msg: { [key: string]: any }): msg is LonkSubscription { + return "subscribed" in msg && msg.subscribed === true; +} + +function isDone(msg: { [key: string]: any }): msg is LonkDone { + return "done" in msg && msg.done === true; +} + +function isProgress(msg: { [key: string]: any }): msg is LonkProgress { + return "ndicom" in msg && Number.isInteger(msg.ndicom); +} + +function isError(msg: { [key: string]: any }): msg is LonkError { + return "error" in msg; +} + +export default LonkSubscriber; +export type { LonkHandlers }; diff --git a/src/api/lonk/de.ts b/src/api/lonk/de.ts new file mode 100644 index 000000000..989599f23 --- /dev/null +++ b/src/api/lonk/de.ts @@ -0,0 +1,85 @@ +/** + * LONK data deserialization using fp-ts. + */ + +import * as E from "fp-ts/Either"; +import { Lonk, LonkUnsubscription } from "./types.ts"; +import { Lazy, pipe } from "fp-ts/function"; +import * as J from "fp-ts/Json"; + +type UnsubscriptionOrLonk = E.Either>; + +function deserialize(data: any): E.Either { + return pipe( + data, + J.parse, + E.mapLeft(() => "Could not parse message as JSON"), + E.flatMap(validateRecord), + E.flatMap(validateUnsubscriptionOrLonk), + ); +} + +function validateUnsubscriptionOrLonk( + obj: J.JsonRecord, +): E.Either { + return pipe( + validateLonk(obj), + E.map(E.right), + E.orElse(curryValidateUnsubscription(obj)), + ); +} + +function curryValidateUnsubscription( + obj: J.JsonRecord, +): Lazy> { + return () => pipe(validateUnsubscription(obj), E.map(E.left)); +} + +function validateLonk(obj: J.JsonRecord): E.Either> { + if (typeof obj.pacs_name !== "string") { + return E.left(`Missing or invalid 'pacs_name' in ${JSON.stringify(obj)}`); + } + if (typeof obj.SeriesInstanceUID !== "string") { + return E.left( + `Missing or invalid 'SeriesInstanceUID' in ${JSON.stringify(obj)}`, + ); + } + if (typeof obj.message !== "object" || jIsArray(obj.message)) { + return E.left(`Missing or invalid 'message' in ${JSON.stringify(obj)}`); + } + // @ts-ignore proper JSON deserialization is too tedious in TypeScript + return E.right(obj); +} + +function validateUnsubscription( + obj: J.JsonRecord, +): E.Either { + if (typeof obj.message !== "object" || jIsArray(obj.message)) { + return E.left(`Missing or invalid 'message' in ${JSON.stringify(obj)}`); + } + if (obj.message?.subscribed === false) { + // @ts-ignore proper JSON deserialization is too tedious in TypeScript + return E.right(obj); + } + return E.left(`Unrecognized 'message' in ${JSON.stringify(obj)}`); +} + +function validateRecord(obj: J.Json): E.Either { + if (obj === null) { + return E.left("obj is null"); + } + if (typeof obj !== "object") { + return E.left("not an object"); + } + if (jIsArray(obj)) { + return E.left("is an array, expected a JsonRecord"); + } + return E.right(obj); +} + +function jIsArray(obj: J.JsonArray | any): obj is J.JsonArray { + return Array.isArray(obj); +} + +export type { UnsubscriptionOrLonk }; +export default deserialize; diff --git a/src/api/lonk/index.ts b/src/api/lonk/index.ts new file mode 100644 index 000000000..11f9295be --- /dev/null +++ b/src/api/lonk/index.ts @@ -0,0 +1,9 @@ +/** + * "Light Oxidicom NotifiKations" over WebSockets (LONK-WS) client. + * + * https://chrisproject.org/docs/oxidicom/lonk-ws + */ + +export { default as useLonk, type UseLonkParams } from "./useLonk.ts"; +export { default as SeriesMap } from "./seriesMap.ts"; +export type { LonkHandlers, SeriesKey } from "./types.ts"; diff --git a/src/api/lonk/seriesMap.test.ts b/src/api/lonk/seriesMap.test.ts new file mode 100644 index 000000000..5e276bca4 --- /dev/null +++ b/src/api/lonk/seriesMap.test.ts @@ -0,0 +1,11 @@ +import { test, expect } from "vitest"; +import SeriesMap from "./seriesMap.ts"; + +test("SeriesMap", () => { + const map = new SeriesMap(); + expect(map.pop("MyPACS", "12345")).toBeNull(); + const data = {}; + map.set("MyPACS", "12345", data); + expect(map.pop("MyPACS", "12345")).toBe(data); + expect(map.pop("MyPACS", "12345")).toBeNull(); +}); diff --git a/src/api/lonk/seriesMap.ts b/src/api/lonk/seriesMap.ts new file mode 100644 index 000000000..55a2b0e79 --- /dev/null +++ b/src/api/lonk/seriesMap.ts @@ -0,0 +1,57 @@ +import { immerable } from "immer"; + +/** + * A wrapper around {@link Map} where the key is (pacs_name, SeriesInstanceUID). + */ +class SeriesMap { + [immerable] = true; + + private readonly map: Map; + + public constructor() { + this.map = new Map(); + } + + /** + * Get a value for a DICOM series. + */ + public get(pacs_name: string, SeriesInstanceUID: string): T | undefined { + const key = this.keyOf(SeriesInstanceUID, pacs_name); + return this.map.get(key); + } + + /** + * Set a value for a DICOM series. + */ + public set(pacs_name: string, SeriesInstanceUID: string, value: T) { + const key = this.keyOf(SeriesInstanceUID, pacs_name); + this.map.set(key, value); + } + + /** + * Get and remove a value for a DICOM series. + */ + public pop(pacs_name: string, SeriesInstanceUID: string): T | null { + const key = this.keyOf(SeriesInstanceUID, pacs_name); + const value = this.map.get(key); + this.map.delete(key); + return value || null; + } + + private keyOf(pacs_name: string, SeriesInstanceUID: string): string { + return JSON.stringify({ SeriesInstanceUID, pacs_name }); + } + + /** + * Get the entries `[pacs_name, SeriesInstanceUID, value]` + */ + public entries(): [string, string, T][] { + // when we upgrade to TS 5.9, use Iterator.map instead of Array.map + return Array.from(this.map.entries()).map(([key, value]) => { + const { pacs_name, SeriesInstanceUID } = JSON.parse(key); + return [pacs_name, SeriesInstanceUID, value]; + }); + } +} + +export default SeriesMap; diff --git a/src/api/lonk/types.ts b/src/api/lonk/types.ts new file mode 100644 index 000000000..a286c3501 --- /dev/null +++ b/src/api/lonk/types.ts @@ -0,0 +1,110 @@ +/** + * LONK-WS JSON types. + * + * Documentation: https://chrisproject.org/docs/oxidicom/lonk-ws#messages + * + * Reference implementation: + * https://github.com/FNNDSC/ChRIS_ultron_backEnd/blob/cf95993886c22530190c23807b57d525f9d51f99/chris_backend/pacsfiles/lonk.py#L45-L102 + */ + +/** + * The metadata which uniquely identifies a DICOM series. + */ +type SeriesKey = { + SeriesInstanceUID: string; + pacs_name: string; +}; + +/** + * LONK "progress" message. + */ +type LonkProgress = { + /** + * Number of DICOM files stored by *oxidicom* so far. + */ + ndicom: number; +}; + +/** + * LONK "error" message. + */ +type LonkError = { + /** + * Error message originating from *oxidicom*. + */ + error: string; +}; + +/** + * LONK "done" message. + */ +type LonkDone = { + done: true; +}; + +/** + * LONK-WS "subscription" response message. + * + * https://chrisproject.org/docs/oxidicom/lonk-ws#lonk-ws-subscription + */ +type LonkSubscription = { + subscribed: true; +}; + +/** + * LONK-WS "unsubscription" response message. + * + * https://chrisproject.org/docs/oxidicom/lonk-ws#unsubscribe + */ +type LonkUnsubscription = { + subscribed: false; +}; + +/** + * Oxidicom notification message data. + */ +type LonkMessageData = LonkDone | LonkProgress | LonkError | LonkSubscription; + +/** + * Notification from oxidicom about a DICOM series. + */ +type Lonk = { + SeriesInstanceUID: string; + pacs_name: string; + message: T; +}; + +/** + * Handler functions for the various types of LONK protocol messages. + */ +type LonkHandlers = { + onDone: (pacs_name: string, SeriesInstanceUID: string) => void; + onProgress: ( + pacs_name: string, + SeriesInstanceUID: string, + ndicom: number, + ) => void; + onError: ( + pacs_name: string, + SeriesInstanceUID: string, + error: string, + ) => void; + /** + * Error deserializing the data. + * @param data + * @param error + */ + onMessageError?: (data: any, error: string) => void; +}; + +export type { + LonkDone, + LonkProgress, + LonkError, + LonkSubscription, + LonkUnsubscription, + LonkMessageData, + Lonk, + LonkHandlers, + SeriesKey, +}; diff --git a/src/api/lonk/useLonk.test.tsx b/src/api/lonk/useLonk.test.tsx new file mode 100644 index 000000000..a3cadb76a --- /dev/null +++ b/src/api/lonk/useLonk.test.tsx @@ -0,0 +1,224 @@ +import { test, expect, vi } from "vitest"; +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import React from "react"; +import Client from "@fnndsc/chrisapi"; +import useLonk, { getWebsocketUrl, UseLonkParams } from "./useLonk.ts"; +import { createMockCubePacsWs } from "../testHelpers.ts"; +import { ReadyState } from "react-use-websocket"; + +type TestLonkComponentProps = Omit & { + getClient: () => Client; +}; + +const TestLonkComponent: React.FC = ({ + getClient, + ...props +}) => { + const client = React.useMemo(() => getClient(), [getClient]); + const lonk = useLonk({ client, ...props }); + const [pacs_name, setPacsName] = React.useState(""); + const [SeriesInstanceUID, setSeriesInstanceUID] = React.useState(""); + const [subscribedPacsName, setSubscribedPacsName] = React.useState(""); + const [subscribedSeriesUid, setSubscribedSeriesUid] = React.useState(""); + const [unsubscribed, setUnsubscribed] = React.useState("false"); + const onSubmit = React.useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + const result = await lonk.subscribe(pacs_name, SeriesInstanceUID); + setSubscribedPacsName(result.pacs_name); + setSubscribedSeriesUid(result.SeriesInstanceUID); + }, + [lonk.subscribe, setSubscribedPacsName, setSubscribedSeriesUid], + ); + const unsubscribe = React.useCallback(async () => { + await lonk.unsubscribeAll(); + setUnsubscribed("true"); + }, [lonk.unsubscribeAll, setUnsubscribed]); + return ( + <> +
{lonk.readyState}
+
+ setPacsName(e.target.value)} + /> + setSeriesInstanceUID(e.target.value)} + /> + +
+
{subscribedPacsName}
+
+ {subscribedSeriesUid} +
+ +
{unsubscribed}
+ + ); +}; + +test("LonkSubscriber", async () => { + const [client, server] = createMockCubePacsWs(32525); + const props = { + getClient: vi.fn(() => client), + onDone: vi.fn(), + onProgress: vi.fn(), + onError: vi.fn(), + onMessageError: vi.fn(), + }; + render(); + await server.connected; + expect(screen.getByTestId("readyState")).toHaveTextContent( + "" + ReadyState.OPEN, + ); + + const SeriesInstanceUID = "1.234.56789"; + const pacs_name = "MyPACS"; + + const subscriptionReceiveAndRespond = async () => { + await expect(server).toReceiveMessage({ + pacs_name, + SeriesInstanceUID, + action: "subscribe", + }); + server.send({ + pacs_name, + SeriesInstanceUID, + message: { subscribed: true }, + }); + }; + + const subscriptionPromise = subscriptionReceiveAndRespond(); + const pacsNameInput = screen.getByTestId("pacs_name"); + const seriesUidInput = screen.getByTestId("SeriesInstanceUID"); + const subscribeForm = screen.getByTestId("subscribe"); + fireEvent.change(pacsNameInput, { target: { value: pacs_name } }); + fireEvent.change(seriesUidInput, { target: { value: SeriesInstanceUID } }); + fireEvent.submit(subscribeForm); + await subscriptionPromise; + await expect + .poll(() => screen.getByTestId("subscribed-pacs_name")) + .toHaveTextContent(pacs_name); + await expect + .poll(() => screen.getByTestId("subscribed-SeriesInstanceUID")) + .toHaveTextContent(SeriesInstanceUID); + + server.send({ + pacs_name, + SeriesInstanceUID, + message: { + ndicom: 48, + }, + }); + expect(props.onProgress).toHaveBeenCalledOnce(); + expect(props.onProgress).toHaveBeenLastCalledWith( + pacs_name, + SeriesInstanceUID, + 48, + ); + + server.send({ + pacs_name, + SeriesInstanceUID, + message: { + ndicom: 88, + }, + }); + expect(props.onProgress).toHaveBeenCalledTimes(2); + expect(props.onProgress).toHaveBeenLastCalledWith( + pacs_name, + SeriesInstanceUID, + 88, + ); + + server.send({ + pacs_name, + SeriesInstanceUID, + message: { + error: "stuck in chimney", + }, + }); + expect(props.onError).toHaveBeenCalledOnce(); + expect(props.onError).toHaveBeenLastCalledWith( + pacs_name, + SeriesInstanceUID, + "stuck in chimney", + ); + + server.send({ + pacs_name, + SeriesInstanceUID, + message: { + done: true, + }, + }); + expect(props.onDone).toHaveBeenCalledOnce(); + expect(props.onDone).toHaveBeenLastCalledWith(pacs_name, SeriesInstanceUID); + + const bogusData = { bogus: "data" }; + server.send(bogusData); + expect(props.onMessageError).toHaveBeenCalledOnce(); + expect(props.onMessageError).toHaveBeenCalledWith( + JSON.stringify(bogusData), + `Missing or invalid 'message' in ${JSON.stringify(bogusData)}`, + ); + + fireEvent.click(screen.getByTestId("unsubscribe")); + await expect(server).toReceiveMessage({ + action: "unsubscribe", + }); + server.send({ message: { subscribed: false } }); + await expect + .poll(() => screen.getByTestId("unsubscribed")) + .toHaveTextContent("true"); + cleanup(); + await server.closed; +}); + +test.each([ + [ + { + url: "http://example.com/api/v1/downloadtokens/9/", + auth: { + token: "fakeauthtoken", + }, + contentType: "application/vnd.collection+json", + data: { + id: 9, + creation_date: "2024-08-27T17:17:28.580683-04:00", + token: "nota.real.jwttoken", + owner_username: "chris", + }, + }, + "ws://example.com/api/v1/pacs/ws/?token=nota.real.jwttoken", + ], + [ + { + url: "https://example.com/api/v1/downloadtokens/9/", + auth: { + token: "fakeauthtoken", + }, + contentType: "application/vnd.collection+json", + data: { + id: 9, + creation_date: "2024-08-27T17:17:28.580683-04:00", + token: "stillnota.real.jwttoken", + owner_username: "chris", + }, + }, + "wss://example.com/api/v1/pacs/ws/?token=stillnota.real.jwttoken", + ], +])("getWebsocketUrl(%o, %s) -> %s", (downloadTokenResponse, expected) => { + // @ts-ignore + let actual = getWebsocketUrl(downloadTokenResponse); + expect(actual).toBe(expected); +}); diff --git a/src/api/lonk/useLonk.ts b/src/api/lonk/useLonk.ts new file mode 100644 index 000000000..9d358c261 --- /dev/null +++ b/src/api/lonk/useLonk.ts @@ -0,0 +1,103 @@ +import Client, { DownloadToken } from "@fnndsc/chrisapi"; +import useWebSocket, { Options, ReadyState } from "react-use-websocket"; +import { LonkHandlers, SeriesKey } from "./types.ts"; +import React from "react"; +import LonkSubscriber from "./LonkSubscriber.ts"; + +/** + * A subset of the options which are passed through to {@link useWebSocket}. + */ +type AllowedOptions = Pick< + Options, + | "onOpen" + | "onClose" + | "onReconnectStop" + | "shouldReconnect" + | "reconnectInterval" + | "reconnectAttempts" + | "retryOnError" +>; + +type UseLonkParams = LonkHandlers & + AllowedOptions & { + client: Client; + onWebsocketError?: Options["onError"]; + }; + +type UseLonkHook = ReturnType & { + /** + * Subscribe to a DICOM series for receive progress notifications. + */ + subscribe: ( + pacs_name: string, + SeriesInstanceUID: string, + ) => Promise; + /** + * Unsubscribe from all notifications. + */ + unsubscribeAll: () => Promise; +}; + +/** + * Implementation of LONK-WS consumer as a React.js hook, based on + * {@link useWebSocket}. + * + * https://chrisproject.org/docs/oxidicom/lonk-ws + */ +function useLonk({ + client, + onDone, + onProgress, + onError, + onMessageError, + onWebsocketError, + ...options +}: UseLonkParams): UseLonkHook { + const getLonkUrl = React.useCallback(async () => { + const downloadToken = await client.createDownloadToken(); + return getWebsocketUrl(downloadToken); + }, [client, getWebsocketUrl]); + const handlers = { onDone, onProgress, onError, onMessageError }; + const [subscriber, _setSubscriber] = React.useState( + new LonkSubscriber(handlers), + ); + const onMessage = React.useCallback( + (event: MessageEvent) => { + subscriber.handle(event.data); + }, + [onProgress, onDone, onError], + ); + const hook = useWebSocket(getLonkUrl, { + ...options, + onError: onWebsocketError, + onMessage, + }); + + const subscribe = React.useCallback( + (pacs_name: string, SeriesInstanceUID: string) => + subscriber.subscribe(pacs_name, SeriesInstanceUID, hook), + [subscriber, hook], + ); + + const unsubscribeAll = React.useCallback( + () => subscriber.unsubscribeAll(hook), + [subscriber, hook], + ); + + return { + ...hook, + subscribe, + unsubscribeAll, + }; +} + +function getWebsocketUrl(downloadTokenResponse: DownloadToken): string { + const token = downloadTokenResponse.data.token; + return downloadTokenResponse.url + .replace(/^http(s?):\/\//, (_match, s) => `ws${s}://`) + .replace(/v1\/downloadtokens\/\d+\//, `v1/pacs/ws/?token=${token}`); +} + +export type { UseLonkParams }; +export { getWebsocketUrl }; +export default useLonk; diff --git a/src/api/pfdcm/client.ts b/src/api/pfdcm/client.ts new file mode 100644 index 000000000..7c1bce08e --- /dev/null +++ b/src/api/pfdcm/client.ts @@ -0,0 +1,249 @@ +/** + * Wrapper for the OpenAPI-generated client, providing better typing. + * + * Note to developers: we want to use some types from fp-ts such as + * `ReadonlyNonEmptyArray` but we don't use `TaskEither` because + * traditional promises and `throw` are more compatible with TanStack + * Query. + */ + +import { + Configuration, + PACSPypxApiV1PACSSyncPypxPostRequest, + PACSQRServicesApi, + PACSSetupServicesApi, + PACSqueryCore, + PACSServiceHandlerApiV1PACSThreadPypxPostRequest, + PACSasync, +} from "./generated"; +import { pipe } from "fp-ts/function"; +import { PypxFind, PypxTag, Series, StudyAndSeries } from "./models.ts"; +import { parse as parseDate } from "date-fns"; +import { + ReadonlyNonEmptyArray, + fromArray as readonlyNonEmptyArrayFromArray, +} from "fp-ts/ReadonlyNonEmptyArray"; +import { match as matchOption } from "fp-ts/Option"; + +/** + * PFDCM client. + */ +class PfdcmClient { + private readonly servicesClient: PACSSetupServicesApi; + private readonly qrClient: PACSQRServicesApi; + constructor(configuration?: Configuration) { + this.servicesClient = new PACSSetupServicesApi(configuration); + this.qrClient = new PACSQRServicesApi(configuration); + } + + /** + * Get list of PACS services which this PFDCM is configured to speak with. + */ + public async getPacsServices(): Promise> { + const services = + await this.servicesClient.serviceListGetApiV1PACSserviceListGet(); + return pipe( + // default service is a useless option added by pfdcm + services.filter((s) => s !== "default"), + readonlyNonEmptyArrayFromArray, + matchOption( + () => { + throw new Error( + `PFDCM is not configured with any services (besides "default")`, + ); + }, + (some) => some, + ), + ); + } + + private async find(service: string, query: PACSqueryCore): Promise { + const params: PACSPypxApiV1PACSSyncPypxPostRequest = { + bodyPACSPypxApiV1PACSSyncPypxPost: { + pACSservice: { + value: service, + }, + listenerService: { + value: "default", + }, + pACSdirective: query, + }, + }; + const data = await this.qrClient.pACSPypxApiV1PACSSyncPypxPost(params); + if (!isFindResponseData(data)) { + throw new Error("Unrecognizable response from PFDCM"); + } + raiseForBadStatus(data); + return data; + } + + /** + * Search for PACS data. + * @param service which PACS service to search for. See {@link PfdcmClient.getPacsServices} + * @param query PACS query + */ + public async query( + service: string, + query: PACSqueryCore, + ): Promise> { + const data = await this.find(service, query); + return simplifyResponse(data); + } + + public async retrieve( + service: string, + query: PACSqueryCore, + ): Promise { + const params: PACSServiceHandlerApiV1PACSThreadPypxPostRequest = { + bodyPACSServiceHandlerApiV1PACSThreadPypxPost: { + pACSservice: { + value: service, + }, + listenerService: { + value: "default", + }, + pACSdirective: { + ...query, + withFeedBack: true, + then: "retrieve", + }, + }, + }; + const res = + await this.qrClient.pACSServiceHandlerApiV1PACSThreadPypxPost(params); + // @ts-expect-error PFDCM OpenAPI spec is incomplete + if (!res.response?.job?.status) { + throw new Error("PYPX job status is missing or false"); + } + return res; + } +} + +function isFindResponseData(data: any): data is PypxFind { + return ( + typeof data.status === "boolean" && "message" in data && "pypx" in data + ); +} + +/** + * Throw an error for any "status" field that is not `true` + * (this is a convention that Rudolph uses for error handling + * instead of HTTP status codes, exceptions, and/or monads). + */ +function raiseForBadStatus(data: PypxFind): PypxFind { + if (!data.status) { + throw new Error("PFDCM response status=false"); + } + if (data.pypx.status !== "success") { + throw new Error("PFDCM response pypx.status=false"); + } + for (const study of data.pypx.data) { + if (!Array.isArray(study.series)) { + continue; + } + for (const series of study.series) { + if (series.status.value !== "success") { + throw new Error( + `PFDCM response pypx...status is false for SeriesInstanceUID=${series?.SeriesInstanceUID?.value}`, + ); + } + } + } + return data; +} + +/** + * Re-organizes the data from pypx's response. + */ +function simplifyResponse(data: PypxFind): ReadonlyArray { + return data.pypx.data.map(simplifyPypxStudyData); +} + +function simplifyPypxStudyData(data: { + [key: string]: PypxTag | ReadonlyArray<{ [key: string]: PypxTag }>; +}): StudyAndSeries { + const study = { + SpecificCharacterSet: getValue(data, "SpecificCharacterSet"), + StudyDate: + "value" in data.StudyDate ? parseDicomDate(data.StudyDate) : null, + AccessionNumber: getValue(data, "AccessionNumber"), + RetrieveAETitle: getValue(data, "RetrieveAETitle"), + ModalitiesInStudy: getValue(data, "ModalitiesInStudy"), + StudyDescription: getValue(data, "StudyDescription"), + PatientName: getValue(data, "PatientName"), + PatientID: getValue(data, "PatientID"), + PatientBirthDate: + "value" in data.PatientBirthDate + ? parseDicomDate(data.PatientBirthDate) + : null, + PatientSex: getValue(data, "PatientSex"), + PatientAge: getValue(data, "PatientAge"), + ProtocolName: getValue(data, "ProtocolName"), + AcquisitionProtocolName: getValue(data, "AcquisitionProtocolName"), + AcquisitionProtocolDescription: getValue( + data, + "AcquisitionProtocolDescription", + ), + StudyInstanceUID: getValue(data, "StudyInstanceUID"), + NumberOfStudyRelatedSeries: getValue(data, "NumberOfStudyRelatedSeries"), + PerformedStationAETitle: getValue(data, "PerformedStationAETitle"), + }; + const series = Array.isArray(data.series) + ? data.series.map(simplifyPypxSeriesData) + : []; + return { study, series }; +} + +function getValue( + data: { [key: string]: PypxTag | ReadonlyArray<{ [key: string]: PypxTag }> }, + name: string, +): string { + if ("value" in data[name]) { + return "" + data[name].value; + } + return ""; +} + +function simplifyPypxSeriesData(data: { [key: string]: PypxTag }): Series { + const parsedNumInstances = + data.NumberOfSeriesRelatedInstances.value === 0 + ? NaN + : parseInt(data.NumberOfSeriesRelatedInstances.value); + const NumberOfSeriesRelatedInstances = Number.isNaN(parsedNumInstances) + ? null + : parsedNumInstances; + return { + SpecificCharacterSet: "" + data.SpecificCharacterSet.value, + StudyDate: "" + data.StudyDate.value, + SeriesDate: "" + data.SeriesDate.value, + AccessionNumber: "" + data.AccessionNumber.value, + RetrieveAETitle: "" + data.RetrieveAETitle.value, + Modality: "" + data.Modality.value, + StudyDescription: "" + data.StudyDescription.value, + SeriesDescription: "" + data.SeriesDescription.value, + PatientName: "" + data.PatientName.value, + PatientID: "" + data.PatientID.value, + PatientBirthDate: parseDicomDate(data.PatientBirthDate), + PatientSex: "" + data.PatientSex.value, + PatientAge: "" + data.PatientAge.value, + ProtocolName: "" + data.ProtocolName.value, + AcquisitionProtocolName: "" + data.AcquisitionProtocolName.value, + AcquisitionProtocolDescription: + "" + data.AcquisitionProtocolDescription.value, + StudyInstanceUID: "" + data.StudyInstanceUID.value, + SeriesInstanceUID: "" + data.SeriesInstanceUID.value, + NumberOfSeriesRelatedInstances, + PerformedStationAETitle: "" + data.PerformedStationAETitle.value, + }; +} + +/** + * Parse a DICOM DateString (DS), which is in YYYYMMDD format. + * + * https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html + */ +function parseDicomDate(tag: PypxTag): Date { + return parseDate("" + tag.value, "yyyyMMdd", new Date()); +} + +export { PfdcmClient }; diff --git a/src/api/pfdcm/generated/.openapi-generator-ignore b/src/api/pfdcm/generated/.openapi-generator-ignore new file mode 100644 index 000000000..7484ee590 --- /dev/null +++ b/src/api/pfdcm/generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/src/api/pfdcm/generated/.openapi-generator/FILES b/src/api/pfdcm/generated/.openapi-generator/FILES new file mode 100644 index 000000000..f0703c692 --- /dev/null +++ b/src/api/pfdcm/generated/.openapi-generator/FILES @@ -0,0 +1,49 @@ +.openapi-generator-ignore +apis/DicomApi.ts +apis/ListenerSubsystemServicesApi.ts +apis/PACSQRServicesApi.ts +apis/PACSSetupServicesApi.ts +apis/PfdcmEnvironmentalDetailApi.ts +apis/SMDBSetupServicesApi.ts +apis/index.ts +index.ts +models/AboutModel.ts +models/BodyPACSPypxApiV1PACSSyncPypxPost.ts +models/BodyPACSServiceHandlerApiV1PACSThreadPypxPost.ts +models/BodyPACSobjPortUpdateApiV1PACSservicePortPost.ts +models/DcmtkCore.ts +models/DcmtkDBPutModel.ts +models/DcmtkDBReturnModel.ts +models/Dicom.ts +models/EchoModel.ts +models/HTTPValidationError.ts +models/HelloModel.ts +models/ListenerDBreturnModel.ts +models/ListenerHandlerStatus.ts +models/LocationInner.ts +models/ModelsListenerModelTime.ts +models/ModelsListenerModelValueStr.ts +models/ModelsPacsQRmodelValueStr.ts +models/ModelsPacsSetupModelTime.ts +models/ModelsPacsSetupModelValueStr.ts +models/ModelsSmdbSetupModelValueStr.ts +models/PACSasync.ts +models/PACSdbPutModel.ts +models/PACSdbReturnModel.ts +models/PACSqueryCore.ts +models/PACSsetupCore.ts +models/SMDBFsConfig.ts +models/SMDBFsCore.ts +models/SMDBFsReturnModel.ts +models/SMDBcubeConfig.ts +models/SMDBcubeCore.ts +models/SMDBcubeReturnModel.ts +models/SMDBswiftConfig.ts +models/SMDBswiftCore.ts +models/SysInfoModel.ts +models/ValidationError.ts +models/XinetdCore.ts +models/XinetdDBPutModel.ts +models/XinetdDBReturnModel.ts +models/index.ts +runtime.ts diff --git a/src/api/pfdcm/generated/.openapi-generator/VERSION b/src/api/pfdcm/generated/.openapi-generator/VERSION new file mode 100644 index 000000000..09a6d3084 --- /dev/null +++ b/src/api/pfdcm/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.8.0 diff --git a/src/api/pfdcm/generated/apis/DicomApi.ts b/src/api/pfdcm/generated/apis/DicomApi.ts new file mode 100644 index 000000000..bdca8314f --- /dev/null +++ b/src/api/pfdcm/generated/apis/DicomApi.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + Dicom, + HTTPValidationError, +} from '../models/index'; +import { + DicomFromJSON, + DicomToJSON, + HTTPValidationErrorFromJSON, + HTTPValidationErrorToJSON, +} from '../models/index'; + +export interface ReadDicomApiV1DicomGetRequest { + mrn: number; +} + +/** + * + */ +export class DicomApi extends runtime.BaseAPI { + + /** + * Fake meaningless response + * Get dicom images for a patient. + */ + async readDicomApiV1DicomGetRaw(requestParameters: ReadDicomApiV1DicomGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['mrn'] == null) { + throw new runtime.RequiredError( + 'mrn', + 'Required parameter "mrn" was null or undefined when calling readDicomApiV1DicomGet().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['mrn'] != null) { + queryParameters['mrn'] = requestParameters['mrn']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/dicom/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => DicomFromJSON(jsonValue)); + } + + /** + * Fake meaningless response + * Get dicom images for a patient. + */ + async readDicomApiV1DicomGet(requestParameters: ReadDicomApiV1DicomGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.readDicomApiV1DicomGetRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/src/api/pfdcm/generated/apis/ListenerSubsystemServicesApi.ts b/src/api/pfdcm/generated/apis/ListenerSubsystemServicesApi.ts new file mode 100644 index 000000000..f65195e4d --- /dev/null +++ b/src/api/pfdcm/generated/apis/ListenerSubsystemServicesApi.ts @@ -0,0 +1,303 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + DcmtkDBPutModel, + DcmtkDBReturnModel, + HTTPValidationError, + ListenerDBreturnModel, + ListenerHandlerStatus, + ModelsListenerModelValueStr, + XinetdDBPutModel, + XinetdDBReturnModel, +} from '../models/index'; +import { + DcmtkDBPutModelFromJSON, + DcmtkDBPutModelToJSON, + DcmtkDBReturnModelFromJSON, + DcmtkDBReturnModelToJSON, + HTTPValidationErrorFromJSON, + HTTPValidationErrorToJSON, + ListenerDBreturnModelFromJSON, + ListenerDBreturnModelToJSON, + ListenerHandlerStatusFromJSON, + ListenerHandlerStatusToJSON, + ModelsListenerModelValueStrFromJSON, + ModelsListenerModelValueStrToJSON, + XinetdDBPutModelFromJSON, + XinetdDBPutModelToJSON, + XinetdDBReturnModelFromJSON, + XinetdDBReturnModelToJSON, +} from '../models/index'; + +export interface ItemPutDcmtkApiV1ListenerListenerObjNameDcmtkPutRequest { + listenerObjName: string; + dcmtkDBPutModel: DcmtkDBPutModel; +} + +export interface ItemPutXinetdApiV1ListenerListenerObjNameXinetdPutRequest { + listenerObjName: string; + xinetdDBPutModel: XinetdDBPutModel; +} + +export interface ListenerGetApiV1ListenerListenerObjNameGetRequest { + listenerObjName: string; +} + +export interface ListenerInitializeApiV1ListenerInitializePostRequest { + modelsListenerModelValueStr: ModelsListenerModelValueStr; +} + +export interface ListenerStatusGetApiV1ListenerStatusListenerObjNameGetRequest { + listenerObjName: string; +} + +/** + * + */ +export class ListenerSubsystemServicesApi extends runtime.BaseAPI { + + /** + * PUT an entire dcmtk object. If the object already exists, overwrite. If it does not exist, append to the space of available objects. Note that overwriting an existing object will replace ALL the `info` fields, thus leaving a default of `\"string\"` will literally put the text `string` for a specific field. Parameters ---------- - `listenerObjName` : internal name of (new) object - `dcmtkInfo` : new values for object internals + * PUT a dcmtk update + */ + async itemPutDcmtkApiV1ListenerListenerObjNameDcmtkPutRaw(requestParameters: ItemPutDcmtkApiV1ListenerListenerObjNameDcmtkPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['listenerObjName'] == null) { + throw new runtime.RequiredError( + 'listenerObjName', + 'Required parameter "listenerObjName" was null or undefined when calling itemPutDcmtkApiV1ListenerListenerObjNameDcmtkPut().' + ); + } + + if (requestParameters['dcmtkDBPutModel'] == null) { + throw new runtime.RequiredError( + 'dcmtkDBPutModel', + 'Required parameter "dcmtkDBPutModel" was null or undefined when calling itemPutDcmtkApiV1ListenerListenerObjNameDcmtkPut().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/listener/{listenerObjName}/dcmtk/`.replace(`{${"listenerObjName"}}`, encodeURIComponent(String(requestParameters['listenerObjName']))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: DcmtkDBPutModelToJSON(requestParameters['dcmtkDBPutModel']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => DcmtkDBReturnModelFromJSON(jsonValue)); + } + + /** + * PUT an entire dcmtk object. If the object already exists, overwrite. If it does not exist, append to the space of available objects. Note that overwriting an existing object will replace ALL the `info` fields, thus leaving a default of `\"string\"` will literally put the text `string` for a specific field. Parameters ---------- - `listenerObjName` : internal name of (new) object - `dcmtkInfo` : new values for object internals + * PUT a dcmtk update + */ + async itemPutDcmtkApiV1ListenerListenerObjNameDcmtkPut(requestParameters: ItemPutDcmtkApiV1ListenerListenerObjNameDcmtkPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.itemPutDcmtkApiV1ListenerListenerObjNameDcmtkPutRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * PUT an entire xinetd object. If the object already exists, overwrite. If it does not exist, append to the space of available objects. Note that overwriting an existing object will replace ALL the `info` fields, thus leaving a default of `\"string\"` will literally put the text `string` for a specific field. Parameters ---------- - `listenerObjName` : internal name of (new) object - `xinetdInfo` : new values for object internals + * PUT an xinetd update + */ + async itemPutXinetdApiV1ListenerListenerObjNameXinetdPutRaw(requestParameters: ItemPutXinetdApiV1ListenerListenerObjNameXinetdPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['listenerObjName'] == null) { + throw new runtime.RequiredError( + 'listenerObjName', + 'Required parameter "listenerObjName" was null or undefined when calling itemPutXinetdApiV1ListenerListenerObjNameXinetdPut().' + ); + } + + if (requestParameters['xinetdDBPutModel'] == null) { + throw new runtime.RequiredError( + 'xinetdDBPutModel', + 'Required parameter "xinetdDBPutModel" was null or undefined when calling itemPutXinetdApiV1ListenerListenerObjNameXinetdPut().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/listener/{listenerObjName}/xinetd/`.replace(`{${"listenerObjName"}}`, encodeURIComponent(String(requestParameters['listenerObjName']))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: XinetdDBPutModelToJSON(requestParameters['xinetdDBPutModel']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => XinetdDBReturnModelFromJSON(jsonValue)); + } + + /** + * PUT an entire xinetd object. If the object already exists, overwrite. If it does not exist, append to the space of available objects. Note that overwriting an existing object will replace ALL the `info` fields, thus leaving a default of `\"string\"` will literally put the text `string` for a specific field. Parameters ---------- - `listenerObjName` : internal name of (new) object - `xinetdInfo` : new values for object internals + * PUT an xinetd update + */ + async itemPutXinetdApiV1ListenerListenerObjNameXinetdPut(requestParameters: ItemPutXinetdApiV1ListenerListenerObjNameXinetdPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.itemPutXinetdApiV1ListenerListenerObjNameXinetdPutRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * GET the information pertinent to a `listenerObjName` (for a list of valid `listenerObjName` GET the `serviceList`) + * GET information for a given listener object + */ + async listenerGetApiV1ListenerListenerObjNameGetRaw(requestParameters: ListenerGetApiV1ListenerListenerObjNameGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['listenerObjName'] == null) { + throw new runtime.RequiredError( + 'listenerObjName', + 'Required parameter "listenerObjName" was null or undefined when calling listenerGetApiV1ListenerListenerObjNameGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/listener/{listenerObjName}/`.replace(`{${"listenerObjName"}}`, encodeURIComponent(String(requestParameters['listenerObjName']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ListenerDBreturnModelFromJSON(jsonValue)); + } + + /** + * GET the information pertinent to a `listenerObjName` (for a list of valid `listenerObjName` GET the `serviceList`) + * GET information for a given listener object + */ + async listenerGetApiV1ListenerListenerObjNameGet(requestParameters: ListenerGetApiV1ListenerListenerObjNameGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listenerGetApiV1ListenerListenerObjNameGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Initialize the listener service for the object __objToInitialize__. Parameters ---------- - `objToInitialize`: name of the listener object to initialize Return ------ - dictionary response from the initialization process NOTE: A return / response model is not specified since the return from the call is variable. + * POST a signal to the listener `objToInitialize`, triggering a self initialization + */ + async listenerInitializeApiV1ListenerInitializePostRaw(requestParameters: ListenerInitializeApiV1ListenerInitializePostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['modelsListenerModelValueStr'] == null) { + throw new runtime.RequiredError( + 'modelsListenerModelValueStr', + 'Required parameter "modelsListenerModelValueStr" was null or undefined when calling listenerInitializeApiV1ListenerInitializePost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/listener/initialize/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ModelsListenerModelValueStrToJSON(requestParameters['modelsListenerModelValueStr']), + }, initOverrides); + + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + } + + /** + * Initialize the listener service for the object __objToInitialize__. Parameters ---------- - `objToInitialize`: name of the listener object to initialize Return ------ - dictionary response from the initialization process NOTE: A return / response model is not specified since the return from the call is variable. + * POST a signal to the listener `objToInitialize`, triggering a self initialization + */ + async listenerInitializeApiV1ListenerInitializePost(requestParameters: ListenerInitializeApiV1ListenerInitializePostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listenerInitializeApiV1ListenerInitializePostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * GET the listener susystem information pertinent to a `listenerObjName`. This information indicates if the subsystem has been initialized and therefore if it is ready to accept incoming data. (for a list of valid `listenerObjName` GET the `serviceList`) + * GET the listener subsystem status of a given listener object + */ + async listenerStatusGetApiV1ListenerStatusListenerObjNameGetRaw(requestParameters: ListenerStatusGetApiV1ListenerStatusListenerObjNameGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['listenerObjName'] == null) { + throw new runtime.RequiredError( + 'listenerObjName', + 'Required parameter "listenerObjName" was null or undefined when calling listenerStatusGetApiV1ListenerStatusListenerObjNameGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/listener/status/{listenerObjName}/`.replace(`{${"listenerObjName"}}`, encodeURIComponent(String(requestParameters['listenerObjName']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ListenerHandlerStatusFromJSON(jsonValue)); + } + + /** + * GET the listener susystem information pertinent to a `listenerObjName`. This information indicates if the subsystem has been initialized and therefore if it is ready to accept incoming data. (for a list of valid `listenerObjName` GET the `serviceList`) + * GET the listener subsystem status of a given listener object + */ + async listenerStatusGetApiV1ListenerStatusListenerObjNameGet(requestParameters: ListenerStatusGetApiV1ListenerStatusListenerObjNameGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listenerStatusGetApiV1ListenerStatusListenerObjNameGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * GET the list of configured PACS services + * GET the list of configured listener services + */ + async serviceListGetApiV1ListenerListGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/listener/list/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * GET the list of configured PACS services + * GET the list of configured listener services + */ + async serviceListGetApiV1ListenerListGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.serviceListGetApiV1ListenerListGetRaw(initOverrides); + return await response.value(); + } + +} diff --git a/src/api/pfdcm/generated/apis/PACSQRServicesApi.ts b/src/api/pfdcm/generated/apis/PACSQRServicesApi.ts new file mode 100644 index 000000000..3fc64594c --- /dev/null +++ b/src/api/pfdcm/generated/apis/PACSQRServicesApi.ts @@ -0,0 +1,127 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + BodyPACSPypxApiV1PACSSyncPypxPost, + BodyPACSServiceHandlerApiV1PACSThreadPypxPost, + HTTPValidationError, + PACSasync, +} from '../models/index'; +import { + BodyPACSPypxApiV1PACSSyncPypxPostFromJSON, + BodyPACSPypxApiV1PACSSyncPypxPostToJSON, + BodyPACSServiceHandlerApiV1PACSThreadPypxPostFromJSON, + BodyPACSServiceHandlerApiV1PACSThreadPypxPostToJSON, + HTTPValidationErrorFromJSON, + HTTPValidationErrorToJSON, + PACSasyncFromJSON, + PACSasyncToJSON, +} from '../models/index'; + +export interface PACSPypxApiV1PACSSyncPypxPostRequest { + bodyPACSPypxApiV1PACSSyncPypxPost: BodyPACSPypxApiV1PACSSyncPypxPost; +} + +export interface PACSServiceHandlerApiV1PACSThreadPypxPostRequest { + bodyPACSServiceHandlerApiV1PACSThreadPypxPost: BodyPACSServiceHandlerApiV1PACSThreadPypxPost; +} + +/** + * + */ +export class PACSQRServicesApi extends runtime.BaseAPI { + + /** + * POST a retrieve to the `PACSservice`, and capture return communication using the `listenerService`. The client will only receive a return payload when the PACSdirective has completed its remote execution. Parameters ---------- - `PACSservice`: name of the internal PACS service to query - `listenerService`: name of the listener service to use locally - `PACSdirective`: the pypx directive object Return ------ - PACSqueryReturnModel + * Use this API route for STATUS operations and any others that block but which are \"short lived\". Since this is a synchronous operation, the call will only return on successful completion of the remote directive. + */ + async pACSPypxApiV1PACSSyncPypxPostRaw(requestParameters: PACSPypxApiV1PACSSyncPypxPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['bodyPACSPypxApiV1PACSSyncPypxPost'] == null) { + throw new runtime.RequiredError( + 'bodyPACSPypxApiV1PACSSyncPypxPost', + 'Required parameter "bodyPACSPypxApiV1PACSSyncPypxPost" was null or undefined when calling pACSPypxApiV1PACSSyncPypxPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/PACS/sync/pypx/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: BodyPACSPypxApiV1PACSSyncPypxPostToJSON(requestParameters['bodyPACSPypxApiV1PACSSyncPypxPost']), + }, initOverrides); + + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + } + + /** + * POST a retrieve to the `PACSservice`, and capture return communication using the `listenerService`. The client will only receive a return payload when the PACSdirective has completed its remote execution. Parameters ---------- - `PACSservice`: name of the internal PACS service to query - `listenerService`: name of the listener service to use locally - `PACSdirective`: the pypx directive object Return ------ - PACSqueryReturnModel + * Use this API route for STATUS operations and any others that block but which are \"short lived\". Since this is a synchronous operation, the call will only return on successful completion of the remote directive. + */ + async pACSPypxApiV1PACSSyncPypxPost(requestParameters: PACSPypxApiV1PACSSyncPypxPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.pACSPypxApiV1PACSSyncPypxPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Handler into PACS calls for long-lived compute (retrieve/push/register) This is very thin and simple dispatching service that will either use the find module API, or will call the find module script. Anectodal testing has shown that the API calls might fail, possibly due to thread pool exhaustion? At time of writing, the CLI calls seem more reliable since they introduce a single-queue concept by explicitly waiting for a CLI px-find process to finish. While this means that status calls are somewhat blocked when a RPR job is in flight, for multiple series pulls, the retrieve/push/register workflow proceeds correctly. Args: PACSservice (pacsQRmodel.ValueStr): The PACS with which to communicate listenerService (pacsQRmodel.ValueStr): The listener service that receives PACS comms PACSdirective (pacsQRmodel.PACSqueryCore): The instructions to the PACS + * Use this API route for RETRIEVE, PUSH, REGISTER operations and any others that might be possibly \"long lived\". The actual processing is dispatched to a separate thread so that the client receives a return immediately. Clients should use a STATUS request on the same payload to determine realtime status of the operation. + */ + async pACSServiceHandlerApiV1PACSThreadPypxPostRaw(requestParameters: PACSServiceHandlerApiV1PACSThreadPypxPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['bodyPACSServiceHandlerApiV1PACSThreadPypxPost'] == null) { + throw new runtime.RequiredError( + 'bodyPACSServiceHandlerApiV1PACSThreadPypxPost', + 'Required parameter "bodyPACSServiceHandlerApiV1PACSThreadPypxPost" was null or undefined when calling pACSServiceHandlerApiV1PACSThreadPypxPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/PACS/thread/pypx/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: BodyPACSServiceHandlerApiV1PACSThreadPypxPostToJSON(requestParameters['bodyPACSServiceHandlerApiV1PACSThreadPypxPost']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PACSasyncFromJSON(jsonValue)); + } + + /** + * Handler into PACS calls for long-lived compute (retrieve/push/register) This is very thin and simple dispatching service that will either use the find module API, or will call the find module script. Anectodal testing has shown that the API calls might fail, possibly due to thread pool exhaustion? At time of writing, the CLI calls seem more reliable since they introduce a single-queue concept by explicitly waiting for a CLI px-find process to finish. While this means that status calls are somewhat blocked when a RPR job is in flight, for multiple series pulls, the retrieve/push/register workflow proceeds correctly. Args: PACSservice (pacsQRmodel.ValueStr): The PACS with which to communicate listenerService (pacsQRmodel.ValueStr): The listener service that receives PACS comms PACSdirective (pacsQRmodel.PACSqueryCore): The instructions to the PACS + * Use this API route for RETRIEVE, PUSH, REGISTER operations and any others that might be possibly \"long lived\". The actual processing is dispatched to a separate thread so that the client receives a return immediately. Clients should use a STATUS request on the same payload to determine realtime status of the operation. + */ + async pACSServiceHandlerApiV1PACSThreadPypxPost(requestParameters: PACSServiceHandlerApiV1PACSThreadPypxPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.pACSServiceHandlerApiV1PACSThreadPypxPostRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/src/api/pfdcm/generated/apis/PACSSetupServicesApi.ts b/src/api/pfdcm/generated/apis/PACSSetupServicesApi.ts new file mode 100644 index 000000000..0d463fc33 --- /dev/null +++ b/src/api/pfdcm/generated/apis/PACSSetupServicesApi.ts @@ -0,0 +1,198 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + BodyPACSobjPortUpdateApiV1PACSservicePortPost, + HTTPValidationError, + PACSdbPutModel, + PACSdbReturnModel, +} from '../models/index'; +import { + BodyPACSobjPortUpdateApiV1PACSservicePortPostFromJSON, + BodyPACSobjPortUpdateApiV1PACSservicePortPostToJSON, + HTTPValidationErrorFromJSON, + HTTPValidationErrorToJSON, + PACSdbPutModelFromJSON, + PACSdbPutModelToJSON, + PACSdbReturnModelFromJSON, + PACSdbReturnModelToJSON, +} from '../models/index'; + +export interface PACSobjPortUpdateApiV1PACSservicePortPostRequest { + bodyPACSobjPortUpdateApiV1PACSservicePortPost: BodyPACSobjPortUpdateApiV1PACSservicePortPost; +} + +export interface PacsSetupGetApiV1PACSservicePACSobjNameGetRequest { + pACSobjName: string; +} + +export interface PacsSetupPutApiV1PACSservicePACSobjNamePutRequest { + pACSobjName: string; + pACSdbPutModel: PACSdbPutModel; +} + +/** + * + */ +export class PACSSetupServicesApi extends runtime.BaseAPI { + + /** + * Update the `server_port` of a given __objToUpdate__. This method is more exemplar than actually useful. Parameters ---------- - `objToUpdate`: name of the internal PACS object to update - `newPort`: port value string to re-assign in the internal object Return ------ - updated model of the `objToUpdate` + * POST a change to the listener `port` of the PACS `objToUpdate` + */ + async pACSobjPortUpdateApiV1PACSservicePortPostRaw(requestParameters: PACSobjPortUpdateApiV1PACSservicePortPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['bodyPACSobjPortUpdateApiV1PACSservicePortPost'] == null) { + throw new runtime.RequiredError( + 'bodyPACSobjPortUpdateApiV1PACSservicePortPost', + 'Required parameter "bodyPACSobjPortUpdateApiV1PACSservicePortPost" was null or undefined when calling pACSobjPortUpdateApiV1PACSservicePortPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/PACSservice/port/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: BodyPACSobjPortUpdateApiV1PACSservicePortPostToJSON(requestParameters['bodyPACSobjPortUpdateApiV1PACSservicePortPost']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PACSdbReturnModelFromJSON(jsonValue)); + } + + /** + * Update the `server_port` of a given __objToUpdate__. This method is more exemplar than actually useful. Parameters ---------- - `objToUpdate`: name of the internal PACS object to update - `newPort`: port value string to re-assign in the internal object Return ------ - updated model of the `objToUpdate` + * POST a change to the listener `port` of the PACS `objToUpdate` + */ + async pACSobjPortUpdateApiV1PACSservicePortPost(requestParameters: PACSobjPortUpdateApiV1PACSservicePortPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.pACSobjPortUpdateApiV1PACSservicePortPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * GET the setup info pertinent to a `pacsSetup` + * GET the information for a given PACS + */ + async pacsSetupGetApiV1PACSservicePACSobjNameGetRaw(requestParameters: PacsSetupGetApiV1PACSservicePACSobjNameGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['pACSobjName'] == null) { + throw new runtime.RequiredError( + 'pACSobjName', + 'Required parameter "pACSobjName" was null or undefined when calling pacsSetupGetApiV1PACSservicePACSobjNameGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/PACSservice/{PACSobjName}/`.replace(`{${"PACSobjName"}}`, encodeURIComponent(String(requestParameters['pACSobjName']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PACSdbReturnModelFromJSON(jsonValue)); + } + + /** + * GET the setup info pertinent to a `pacsSetup` + * GET the information for a given PACS + */ + async pacsSetupGetApiV1PACSservicePACSobjNameGet(requestParameters: PacsSetupGetApiV1PACSservicePACSobjNameGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.pacsSetupGetApiV1PACSservicePACSobjNameGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * PUT an entire object. If the object already exists, overwrite. If it does not exist, append to the space of available objects. Note that overwriting an existing object will replace ALL the `info` fields, thus leaving a default of `\"string\"` will literally put the text `string` for a specific field. Parameters ---------- - `PACSobjName` : internal name of (new) object - `PACSsetupData` : new values for object internals + * PUT information to a (possibly new) PACS object + */ + async pacsSetupPutApiV1PACSservicePACSobjNamePutRaw(requestParameters: PacsSetupPutApiV1PACSservicePACSobjNamePutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['pACSobjName'] == null) { + throw new runtime.RequiredError( + 'pACSobjName', + 'Required parameter "pACSobjName" was null or undefined when calling pacsSetupPutApiV1PACSservicePACSobjNamePut().' + ); + } + + if (requestParameters['pACSdbPutModel'] == null) { + throw new runtime.RequiredError( + 'pACSdbPutModel', + 'Required parameter "pACSdbPutModel" was null or undefined when calling pacsSetupPutApiV1PACSservicePACSobjNamePut().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/PACSservice/{PACSobjName}/`.replace(`{${"PACSobjName"}}`, encodeURIComponent(String(requestParameters['pACSobjName']))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: PACSdbPutModelToJSON(requestParameters['pACSdbPutModel']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PACSdbReturnModelFromJSON(jsonValue)); + } + + /** + * PUT an entire object. If the object already exists, overwrite. If it does not exist, append to the space of available objects. Note that overwriting an existing object will replace ALL the `info` fields, thus leaving a default of `\"string\"` will literally put the text `string` for a specific field. Parameters ---------- - `PACSobjName` : internal name of (new) object - `PACSsetupData` : new values for object internals + * PUT information to a (possibly new) PACS object + */ + async pacsSetupPutApiV1PACSservicePACSobjNamePut(requestParameters: PacsSetupPutApiV1PACSservicePACSobjNamePutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.pacsSetupPutApiV1PACSservicePACSobjNamePutRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * GET the list of configured PACS services + * GET the list of configured PACS services + */ + async serviceListGetApiV1PACSserviceListGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/PACSservice/list/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * GET the list of configured PACS services + * GET the list of configured PACS services + */ + async serviceListGetApiV1PACSserviceListGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.serviceListGetApiV1PACSserviceListGetRaw(initOverrides); + return await response.value(); + } + +} diff --git a/src/api/pfdcm/generated/apis/PfdcmEnvironmentalDetailApi.ts b/src/api/pfdcm/generated/apis/PfdcmEnvironmentalDetailApi.ts new file mode 100644 index 000000000..7d001e6b4 --- /dev/null +++ b/src/api/pfdcm/generated/apis/PfdcmEnvironmentalDetailApi.ts @@ -0,0 +1,100 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + AboutModel, + HTTPValidationError, + HelloModel, +} from '../models/index'; +import { + AboutModelFromJSON, + AboutModelToJSON, + HTTPValidationErrorFromJSON, + HTTPValidationErrorToJSON, + HelloModelFromJSON, + HelloModelToJSON, +} from '../models/index'; + +export interface ReadHelloApiV1HelloGetRequest { + echoBack?: string; +} + +/** + * + */ +export class PfdcmEnvironmentalDetailApi extends runtime.BaseAPI { + + /** + * A description of this service. + * Read About + */ + async readAboutApiV1AboutGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/about/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AboutModelFromJSON(jsonValue)); + } + + /** + * A description of this service. + * Read About + */ + async readAboutApiV1AboutGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.readAboutApiV1AboutGetRaw(initOverrides); + return await response.value(); + } + + /** + * Produce some information like the OG pfcon + * Read Hello + */ + async readHelloApiV1HelloGetRaw(requestParameters: ReadHelloApiV1HelloGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + if (requestParameters['echoBack'] != null) { + queryParameters['echoBack'] = requestParameters['echoBack']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/hello/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => HelloModelFromJSON(jsonValue)); + } + + /** + * Produce some information like the OG pfcon + * Read Hello + */ + async readHelloApiV1HelloGet(requestParameters: ReadHelloApiV1HelloGetRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.readHelloApiV1HelloGetRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/src/api/pfdcm/generated/apis/SMDBSetupServicesApi.ts b/src/api/pfdcm/generated/apis/SMDBSetupServicesApi.ts new file mode 100644 index 000000000..555438937 --- /dev/null +++ b/src/api/pfdcm/generated/apis/SMDBSetupServicesApi.ts @@ -0,0 +1,305 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + HTTPValidationError, + SMDBFsConfig, + SMDBFsReturnModel, + SMDBcubeConfig, + SMDBcubeReturnModel, + SMDBswiftConfig, +} from '../models/index'; +import { + HTTPValidationErrorFromJSON, + HTTPValidationErrorToJSON, + SMDBFsConfigFromJSON, + SMDBFsConfigToJSON, + SMDBFsReturnModelFromJSON, + SMDBFsReturnModelToJSON, + SMDBcubeConfigFromJSON, + SMDBcubeConfigToJSON, + SMDBcubeReturnModelFromJSON, + SMDBcubeReturnModelToJSON, + SMDBswiftConfigFromJSON, + SMDBswiftConfigToJSON, +} from '../models/index'; + +export interface CubeResourceGetApiV1SMDBCUBECubeResourceGetRequest { + cubeResource: string; +} + +export interface SMDBobjCubeUpdateApiV1SMDBCUBEPostRequest { + sMDBcubeConfig: SMDBcubeConfig; +} + +export interface SMDBobjFsUpdateApiV1SMDBFSPostRequest { + sMDBFsConfig: SMDBFsConfig; +} + +export interface SMDBobjSwiftUpdateApiV1SMDBSwiftPostRequest { + sMDBswiftConfig: SMDBswiftConfig; +} + +export interface StorageResourceGetApiV1SMDBStorageStorageResourceGetRequest { + storageResource: string; +} + +/** + * + */ +export class SMDBSetupServicesApi extends runtime.BaseAPI { + + /** + * GET the list of configured SMDB CUBE services + * GET the list of configured SMDB CUBE services + */ + async cubeListGetApiV1SMDBCUBEListGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/SMDB/CUBE/list/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * GET the list of configured SMDB CUBE services + * GET the list of configured SMDB CUBE services + */ + async cubeListGetApiV1SMDBCUBEListGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.cubeListGetApiV1SMDBCUBEListGetRaw(initOverrides); + return await response.value(); + } + + /** + * GET detail info on a given SMDB CUBE resource + * GET detail on a specific CUBE resource + */ + async cubeResourceGetApiV1SMDBCUBECubeResourceGetRaw(requestParameters: CubeResourceGetApiV1SMDBCUBECubeResourceGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['cubeResource'] == null) { + throw new runtime.RequiredError( + 'cubeResource', + 'Required parameter "cubeResource" was null or undefined when calling cubeResourceGetApiV1SMDBCUBECubeResourceGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/SMDB/CUBE/{cubeResource}/`.replace(`{${"cubeResource"}}`, encodeURIComponent(String(requestParameters['cubeResource']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => SMDBcubeReturnModelFromJSON(jsonValue)); + } + + /** + * GET detail info on a given SMDB CUBE resource + * GET detail on a specific CUBE resource + */ + async cubeResourceGetApiV1SMDBCUBECubeResourceGet(requestParameters: CubeResourceGetApiV1SMDBCUBECubeResourceGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.cubeResourceGetApiV1SMDBCUBECubeResourceGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update a CUBE resource within the SMDB module. Parameters ---------- - `swiftData`: an object with a name that defines a CUBE resource Return ------ - updated `CUBEdata` + * POST an update to a CUBE resource in the pypx SMDB object + */ + async sMDBobjCubeUpdateApiV1SMDBCUBEPostRaw(requestParameters: SMDBobjCubeUpdateApiV1SMDBCUBEPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['sMDBcubeConfig'] == null) { + throw new runtime.RequiredError( + 'sMDBcubeConfig', + 'Required parameter "sMDBcubeConfig" was null or undefined when calling sMDBobjCubeUpdateApiV1SMDBCUBEPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/SMDB/CUBE/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: SMDBcubeConfigToJSON(requestParameters['sMDBcubeConfig']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => SMDBcubeReturnModelFromJSON(jsonValue)); + } + + /** + * Update a CUBE resource within the SMDB module. Parameters ---------- - `swiftData`: an object with a name that defines a CUBE resource Return ------ - updated `CUBEdata` + * POST an update to a CUBE resource in the pypx SMDB object + */ + async sMDBobjCubeUpdateApiV1SMDBCUBEPost(requestParameters: SMDBobjCubeUpdateApiV1SMDBCUBEPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.sMDBobjCubeUpdateApiV1SMDBCUBEPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update a FS storage resource within the SMDB module. Parameters ---------- - `FSData`: an object with a name that defines a swift resource Return ------ - updated `FSData` + * POST an update to a FS storage resource in the pypx SMDB object + */ + async sMDBobjFsUpdateApiV1SMDBFSPostRaw(requestParameters: SMDBobjFsUpdateApiV1SMDBFSPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['sMDBFsConfig'] == null) { + throw new runtime.RequiredError( + 'sMDBFsConfig', + 'Required parameter "sMDBFsConfig" was null or undefined when calling sMDBobjFsUpdateApiV1SMDBFSPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/SMDB/FS/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: SMDBFsConfigToJSON(requestParameters['sMDBFsConfig']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => SMDBFsReturnModelFromJSON(jsonValue)); + } + + /** + * Update a FS storage resource within the SMDB module. Parameters ---------- - `FSData`: an object with a name that defines a swift resource Return ------ - updated `FSData` + * POST an update to a FS storage resource in the pypx SMDB object + */ + async sMDBobjFsUpdateApiV1SMDBFSPost(requestParameters: SMDBobjFsUpdateApiV1SMDBFSPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.sMDBobjFsUpdateApiV1SMDBFSPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update a swift resource within the SMDB module. Parameters ---------- - `swiftData`: an object with a name that defines a swift resource Return ------ - updated `swiftData` + * POST an update to a swift resource in the pypx SMDB object + */ + async sMDBobjSwiftUpdateApiV1SMDBSwiftPostRaw(requestParameters: SMDBobjSwiftUpdateApiV1SMDBSwiftPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['sMDBswiftConfig'] == null) { + throw new runtime.RequiredError( + 'sMDBswiftConfig', + 'Required parameter "sMDBswiftConfig" was null or undefined when calling sMDBobjSwiftUpdateApiV1SMDBSwiftPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/SMDB/swift/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: SMDBswiftConfigToJSON(requestParameters['sMDBswiftConfig']), + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * Update a swift resource within the SMDB module. Parameters ---------- - `swiftData`: an object with a name that defines a swift resource Return ------ - updated `swiftData` + * POST an update to a swift resource in the pypx SMDB object + */ + async sMDBobjSwiftUpdateApiV1SMDBSwiftPost(requestParameters: SMDBobjSwiftUpdateApiV1SMDBSwiftPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.sMDBobjSwiftUpdateApiV1SMDBSwiftPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * GET the list of configured SMDB storage services + * GET the list of configured SMDB storage services + */ + async storageListGetApiV1SMDBStorageListGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/SMDB/storage/list/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * GET the list of configured SMDB storage services + * GET the list of configured SMDB storage services + */ + async storageListGetApiV1SMDBStorageListGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.storageListGetApiV1SMDBStorageListGetRaw(initOverrides); + return await response.value(); + } + + /** + * GET detail info on a given SMDB storage resource + * GET detail on a specific storage resource + */ + async storageResourceGetApiV1SMDBStorageStorageResourceGetRaw(requestParameters: StorageResourceGetApiV1SMDBStorageStorageResourceGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['storageResource'] == null) { + throw new runtime.RequiredError( + 'storageResource', + 'Required parameter "storageResource" was null or undefined when calling storageResourceGetApiV1SMDBStorageStorageResourceGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/SMDB/storage/{storageResource}/`.replace(`{${"storageResource"}}`, encodeURIComponent(String(requestParameters['storageResource']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * GET detail info on a given SMDB storage resource + * GET detail on a specific storage resource + */ + async storageResourceGetApiV1SMDBStorageStorageResourceGet(requestParameters: StorageResourceGetApiV1SMDBStorageStorageResourceGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.storageResourceGetApiV1SMDBStorageStorageResourceGetRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/src/api/pfdcm/generated/apis/index.ts b/src/api/pfdcm/generated/apis/index.ts new file mode 100644 index 000000000..4c1bb7d95 --- /dev/null +++ b/src/api/pfdcm/generated/apis/index.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './DicomApi'; +export * from './ListenerSubsystemServicesApi'; +export * from './PACSQRServicesApi'; +export * from './PACSSetupServicesApi'; +export * from './PfdcmEnvironmentalDetailApi'; +export * from './SMDBSetupServicesApi'; diff --git a/src/api/pfdcm/generated/index.ts b/src/api/pfdcm/generated/index.ts new file mode 100644 index 000000000..bebe8bbbe --- /dev/null +++ b/src/api/pfdcm/generated/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; diff --git a/src/api/pfdcm/generated/models/AboutModel.ts b/src/api/pfdcm/generated/models/AboutModel.ts new file mode 100644 index 000000000..cc372b602 --- /dev/null +++ b/src/api/pfdcm/generated/models/AboutModel.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface AboutModel + */ +export interface AboutModel { + /** + * + * @type {string} + * @memberof AboutModel + */ + name?: string; + /** + * + * @type {string} + * @memberof AboutModel + */ + about?: string; + /** + * + * @type {string} + * @memberof AboutModel + */ + version?: string; +} + +/** + * Check if a given object implements the AboutModel interface. + */ +export function instanceOfAboutModel(value: object): value is AboutModel { + return true; +} + +export function AboutModelFromJSON(json: any): AboutModel { + return AboutModelFromJSONTyped(json, false); +} + +export function AboutModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): AboutModel { + if (json == null) { + return json; + } + return { + + 'name': json['name'] == null ? undefined : json['name'], + 'about': json['about'] == null ? undefined : json['about'], + 'version': json['version'] == null ? undefined : json['version'], + }; +} + +export function AboutModelToJSON(value?: AboutModel | null): any { + if (value == null) { + return value; + } + return { + + 'name': value['name'], + 'about': value['about'], + 'version': value['version'], + }; +} + diff --git a/src/api/pfdcm/generated/models/BodyPACSPypxApiV1PACSSyncPypxPost.ts b/src/api/pfdcm/generated/models/BodyPACSPypxApiV1PACSSyncPypxPost.ts new file mode 100644 index 000000000..fc5ef707a --- /dev/null +++ b/src/api/pfdcm/generated/models/BodyPACSPypxApiV1PACSSyncPypxPost.ts @@ -0,0 +1,92 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { PACSqueryCore } from './PACSqueryCore'; +import { + PACSqueryCoreFromJSON, + PACSqueryCoreFromJSONTyped, + PACSqueryCoreToJSON, +} from './PACSqueryCore'; +import type { ModelsPacsQRmodelValueStr } from './ModelsPacsQRmodelValueStr'; +import { + ModelsPacsQRmodelValueStrFromJSON, + ModelsPacsQRmodelValueStrFromJSONTyped, + ModelsPacsQRmodelValueStrToJSON, +} from './ModelsPacsQRmodelValueStr'; + +/** + * + * @export + * @interface BodyPACSPypxApiV1PACSSyncPypxPost + */ +export interface BodyPACSPypxApiV1PACSSyncPypxPost { + /** + * + * @type {ModelsPacsQRmodelValueStr} + * @memberof BodyPACSPypxApiV1PACSSyncPypxPost + */ + pACSservice: ModelsPacsQRmodelValueStr; + /** + * + * @type {ModelsPacsQRmodelValueStr} + * @memberof BodyPACSPypxApiV1PACSSyncPypxPost + */ + listenerService: ModelsPacsQRmodelValueStr; + /** + * + * @type {PACSqueryCore} + * @memberof BodyPACSPypxApiV1PACSSyncPypxPost + */ + pACSdirective: PACSqueryCore; +} + +/** + * Check if a given object implements the BodyPACSPypxApiV1PACSSyncPypxPost interface. + */ +export function instanceOfBodyPACSPypxApiV1PACSSyncPypxPost(value: object): value is BodyPACSPypxApiV1PACSSyncPypxPost { + if (!('pACSservice' in value) || value['pACSservice'] === undefined) return false; + if (!('listenerService' in value) || value['listenerService'] === undefined) return false; + if (!('pACSdirective' in value) || value['pACSdirective'] === undefined) return false; + return true; +} + +export function BodyPACSPypxApiV1PACSSyncPypxPostFromJSON(json: any): BodyPACSPypxApiV1PACSSyncPypxPost { + return BodyPACSPypxApiV1PACSSyncPypxPostFromJSONTyped(json, false); +} + +export function BodyPACSPypxApiV1PACSSyncPypxPostFromJSONTyped(json: any, ignoreDiscriminator: boolean): BodyPACSPypxApiV1PACSSyncPypxPost { + if (json == null) { + return json; + } + return { + + 'pACSservice': ModelsPacsQRmodelValueStrFromJSON(json['PACSservice']), + 'listenerService': ModelsPacsQRmodelValueStrFromJSON(json['listenerService']), + 'pACSdirective': PACSqueryCoreFromJSON(json['PACSdirective']), + }; +} + +export function BodyPACSPypxApiV1PACSSyncPypxPostToJSON(value?: BodyPACSPypxApiV1PACSSyncPypxPost | null): any { + if (value == null) { + return value; + } + return { + + 'PACSservice': ModelsPacsQRmodelValueStrToJSON(value['pACSservice']), + 'listenerService': ModelsPacsQRmodelValueStrToJSON(value['listenerService']), + 'PACSdirective': PACSqueryCoreToJSON(value['pACSdirective']), + }; +} + diff --git a/src/api/pfdcm/generated/models/BodyPACSServiceHandlerApiV1PACSThreadPypxPost.ts b/src/api/pfdcm/generated/models/BodyPACSServiceHandlerApiV1PACSThreadPypxPost.ts new file mode 100644 index 000000000..dd9b1085f --- /dev/null +++ b/src/api/pfdcm/generated/models/BodyPACSServiceHandlerApiV1PACSThreadPypxPost.ts @@ -0,0 +1,92 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { PACSqueryCore } from './PACSqueryCore'; +import { + PACSqueryCoreFromJSON, + PACSqueryCoreFromJSONTyped, + PACSqueryCoreToJSON, +} from './PACSqueryCore'; +import type { ModelsPacsQRmodelValueStr } from './ModelsPacsQRmodelValueStr'; +import { + ModelsPacsQRmodelValueStrFromJSON, + ModelsPacsQRmodelValueStrFromJSONTyped, + ModelsPacsQRmodelValueStrToJSON, +} from './ModelsPacsQRmodelValueStr'; + +/** + * + * @export + * @interface BodyPACSServiceHandlerApiV1PACSThreadPypxPost + */ +export interface BodyPACSServiceHandlerApiV1PACSThreadPypxPost { + /** + * + * @type {ModelsPacsQRmodelValueStr} + * @memberof BodyPACSServiceHandlerApiV1PACSThreadPypxPost + */ + pACSservice: ModelsPacsQRmodelValueStr; + /** + * + * @type {ModelsPacsQRmodelValueStr} + * @memberof BodyPACSServiceHandlerApiV1PACSThreadPypxPost + */ + listenerService: ModelsPacsQRmodelValueStr; + /** + * + * @type {PACSqueryCore} + * @memberof BodyPACSServiceHandlerApiV1PACSThreadPypxPost + */ + pACSdirective: PACSqueryCore; +} + +/** + * Check if a given object implements the BodyPACSServiceHandlerApiV1PACSThreadPypxPost interface. + */ +export function instanceOfBodyPACSServiceHandlerApiV1PACSThreadPypxPost(value: object): value is BodyPACSServiceHandlerApiV1PACSThreadPypxPost { + if (!('pACSservice' in value) || value['pACSservice'] === undefined) return false; + if (!('listenerService' in value) || value['listenerService'] === undefined) return false; + if (!('pACSdirective' in value) || value['pACSdirective'] === undefined) return false; + return true; +} + +export function BodyPACSServiceHandlerApiV1PACSThreadPypxPostFromJSON(json: any): BodyPACSServiceHandlerApiV1PACSThreadPypxPost { + return BodyPACSServiceHandlerApiV1PACSThreadPypxPostFromJSONTyped(json, false); +} + +export function BodyPACSServiceHandlerApiV1PACSThreadPypxPostFromJSONTyped(json: any, ignoreDiscriminator: boolean): BodyPACSServiceHandlerApiV1PACSThreadPypxPost { + if (json == null) { + return json; + } + return { + + 'pACSservice': ModelsPacsQRmodelValueStrFromJSON(json['PACSservice']), + 'listenerService': ModelsPacsQRmodelValueStrFromJSON(json['listenerService']), + 'pACSdirective': PACSqueryCoreFromJSON(json['PACSdirective']), + }; +} + +export function BodyPACSServiceHandlerApiV1PACSThreadPypxPostToJSON(value?: BodyPACSServiceHandlerApiV1PACSThreadPypxPost | null): any { + if (value == null) { + return value; + } + return { + + 'PACSservice': ModelsPacsQRmodelValueStrToJSON(value['pACSservice']), + 'listenerService': ModelsPacsQRmodelValueStrToJSON(value['listenerService']), + 'PACSdirective': PACSqueryCoreToJSON(value['pACSdirective']), + }; +} + diff --git a/src/api/pfdcm/generated/models/BodyPACSobjPortUpdateApiV1PACSservicePortPost.ts b/src/api/pfdcm/generated/models/BodyPACSobjPortUpdateApiV1PACSservicePortPost.ts new file mode 100644 index 000000000..b139ee347 --- /dev/null +++ b/src/api/pfdcm/generated/models/BodyPACSobjPortUpdateApiV1PACSservicePortPost.ts @@ -0,0 +1,77 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsPacsSetupModelValueStr } from './ModelsPacsSetupModelValueStr'; +import { + ModelsPacsSetupModelValueStrFromJSON, + ModelsPacsSetupModelValueStrFromJSONTyped, + ModelsPacsSetupModelValueStrToJSON, +} from './ModelsPacsSetupModelValueStr'; + +/** + * + * @export + * @interface BodyPACSobjPortUpdateApiV1PACSservicePortPost + */ +export interface BodyPACSobjPortUpdateApiV1PACSservicePortPost { + /** + * + * @type {ModelsPacsSetupModelValueStr} + * @memberof BodyPACSobjPortUpdateApiV1PACSservicePortPost + */ + objToUpdate: ModelsPacsSetupModelValueStr; + /** + * + * @type {ModelsPacsSetupModelValueStr} + * @memberof BodyPACSobjPortUpdateApiV1PACSservicePortPost + */ + newPort: ModelsPacsSetupModelValueStr; +} + +/** + * Check if a given object implements the BodyPACSobjPortUpdateApiV1PACSservicePortPost interface. + */ +export function instanceOfBodyPACSobjPortUpdateApiV1PACSservicePortPost(value: object): value is BodyPACSobjPortUpdateApiV1PACSservicePortPost { + if (!('objToUpdate' in value) || value['objToUpdate'] === undefined) return false; + if (!('newPort' in value) || value['newPort'] === undefined) return false; + return true; +} + +export function BodyPACSobjPortUpdateApiV1PACSservicePortPostFromJSON(json: any): BodyPACSobjPortUpdateApiV1PACSservicePortPost { + return BodyPACSobjPortUpdateApiV1PACSservicePortPostFromJSONTyped(json, false); +} + +export function BodyPACSobjPortUpdateApiV1PACSservicePortPostFromJSONTyped(json: any, ignoreDiscriminator: boolean): BodyPACSobjPortUpdateApiV1PACSservicePortPost { + if (json == null) { + return json; + } + return { + + 'objToUpdate': ModelsPacsSetupModelValueStrFromJSON(json['objToUpdate']), + 'newPort': ModelsPacsSetupModelValueStrFromJSON(json['newPort']), + }; +} + +export function BodyPACSobjPortUpdateApiV1PACSservicePortPostToJSON(value?: BodyPACSobjPortUpdateApiV1PACSservicePortPost | null): any { + if (value == null) { + return value; + } + return { + + 'objToUpdate': ModelsPacsSetupModelValueStrToJSON(value['objToUpdate']), + 'newPort': ModelsPacsSetupModelValueStrToJSON(value['newPort']), + }; +} + diff --git a/src/api/pfdcm/generated/models/DcmtkCore.ts b/src/api/pfdcm/generated/models/DcmtkCore.ts new file mode 100644 index 000000000..dfc5011f1 --- /dev/null +++ b/src/api/pfdcm/generated/models/DcmtkCore.ts @@ -0,0 +1,106 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface DcmtkCore + */ +export interface DcmtkCore { + /** + * + * @type {string} + * @memberof DcmtkCore + */ + storescu: string; + /** + * + * @type {string} + * @memberof DcmtkCore + */ + storescp: string; + /** + * + * @type {string} + * @memberof DcmtkCore + */ + findscu: string; + /** + * + * @type {string} + * @memberof DcmtkCore + */ + movescu: string; + /** + * + * @type {string} + * @memberof DcmtkCore + */ + echoscu: string; + /** + * + * @type {string} + * @memberof DcmtkCore + */ + receiver: string; +} + +/** + * Check if a given object implements the DcmtkCore interface. + */ +export function instanceOfDcmtkCore(value: object): value is DcmtkCore { + if (!('storescu' in value) || value['storescu'] === undefined) return false; + if (!('storescp' in value) || value['storescp'] === undefined) return false; + if (!('findscu' in value) || value['findscu'] === undefined) return false; + if (!('movescu' in value) || value['movescu'] === undefined) return false; + if (!('echoscu' in value) || value['echoscu'] === undefined) return false; + if (!('receiver' in value) || value['receiver'] === undefined) return false; + return true; +} + +export function DcmtkCoreFromJSON(json: any): DcmtkCore { + return DcmtkCoreFromJSONTyped(json, false); +} + +export function DcmtkCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): DcmtkCore { + if (json == null) { + return json; + } + return { + + 'storescu': json['storescu'], + 'storescp': json['storescp'], + 'findscu': json['findscu'], + 'movescu': json['movescu'], + 'echoscu': json['echoscu'], + 'receiver': json['receiver'], + }; +} + +export function DcmtkCoreToJSON(value?: DcmtkCore | null): any { + if (value == null) { + return value; + } + return { + + 'storescu': value['storescu'], + 'storescp': value['storescp'], + 'findscu': value['findscu'], + 'movescu': value['movescu'], + 'echoscu': value['echoscu'], + 'receiver': value['receiver'], + }; +} + diff --git a/src/api/pfdcm/generated/models/DcmtkDBPutModel.ts b/src/api/pfdcm/generated/models/DcmtkDBPutModel.ts new file mode 100644 index 000000000..5f3dd82ae --- /dev/null +++ b/src/api/pfdcm/generated/models/DcmtkDBPutModel.ts @@ -0,0 +1,68 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { DcmtkCore } from './DcmtkCore'; +import { + DcmtkCoreFromJSON, + DcmtkCoreFromJSONTyped, + DcmtkCoreToJSON, +} from './DcmtkCore'; + +/** + * + * @export + * @interface DcmtkDBPutModel + */ +export interface DcmtkDBPutModel { + /** + * + * @type {DcmtkCore} + * @memberof DcmtkDBPutModel + */ + info: DcmtkCore; +} + +/** + * Check if a given object implements the DcmtkDBPutModel interface. + */ +export function instanceOfDcmtkDBPutModel(value: object): value is DcmtkDBPutModel { + if (!('info' in value) || value['info'] === undefined) return false; + return true; +} + +export function DcmtkDBPutModelFromJSON(json: any): DcmtkDBPutModel { + return DcmtkDBPutModelFromJSONTyped(json, false); +} + +export function DcmtkDBPutModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): DcmtkDBPutModel { + if (json == null) { + return json; + } + return { + + 'info': DcmtkCoreFromJSON(json['info']), + }; +} + +export function DcmtkDBPutModelToJSON(value?: DcmtkDBPutModel | null): any { + if (value == null) { + return value; + } + return { + + 'info': DcmtkCoreToJSON(value['info']), + }; +} + diff --git a/src/api/pfdcm/generated/models/DcmtkDBReturnModel.ts b/src/api/pfdcm/generated/models/DcmtkDBReturnModel.ts new file mode 100644 index 000000000..ca92687ef --- /dev/null +++ b/src/api/pfdcm/generated/models/DcmtkDBReturnModel.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsListenerModelTime } from './ModelsListenerModelTime'; +import { + ModelsListenerModelTimeFromJSON, + ModelsListenerModelTimeFromJSONTyped, + ModelsListenerModelTimeToJSON, +} from './ModelsListenerModelTime'; +import type { DcmtkCore } from './DcmtkCore'; +import { + DcmtkCoreFromJSON, + DcmtkCoreFromJSONTyped, + DcmtkCoreToJSON, +} from './DcmtkCore'; + +/** + * + * @export + * @interface DcmtkDBReturnModel + */ +export interface DcmtkDBReturnModel { + /** + * + * @type {DcmtkCore} + * @memberof DcmtkDBReturnModel + */ + info: DcmtkCore; + /** + * + * @type {ModelsListenerModelTime} + * @memberof DcmtkDBReturnModel + */ + timeCreated: ModelsListenerModelTime; + /** + * + * @type {ModelsListenerModelTime} + * @memberof DcmtkDBReturnModel + */ + timeModified: ModelsListenerModelTime; + /** + * + * @type {string} + * @memberof DcmtkDBReturnModel + */ + message: string; +} + +/** + * Check if a given object implements the DcmtkDBReturnModel interface. + */ +export function instanceOfDcmtkDBReturnModel(value: object): value is DcmtkDBReturnModel { + if (!('info' in value) || value['info'] === undefined) return false; + if (!('timeCreated' in value) || value['timeCreated'] === undefined) return false; + if (!('timeModified' in value) || value['timeModified'] === undefined) return false; + if (!('message' in value) || value['message'] === undefined) return false; + return true; +} + +export function DcmtkDBReturnModelFromJSON(json: any): DcmtkDBReturnModel { + return DcmtkDBReturnModelFromJSONTyped(json, false); +} + +export function DcmtkDBReturnModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): DcmtkDBReturnModel { + if (json == null) { + return json; + } + return { + + 'info': DcmtkCoreFromJSON(json['info']), + 'timeCreated': ModelsListenerModelTimeFromJSON(json['time_created']), + 'timeModified': ModelsListenerModelTimeFromJSON(json['time_modified']), + 'message': json['message'], + }; +} + +export function DcmtkDBReturnModelToJSON(value?: DcmtkDBReturnModel | null): any { + if (value == null) { + return value; + } + return { + + 'info': DcmtkCoreToJSON(value['info']), + 'time_created': ModelsListenerModelTimeToJSON(value['timeCreated']), + 'time_modified': ModelsListenerModelTimeToJSON(value['timeModified']), + 'message': value['message'], + }; +} + diff --git a/src/api/pfdcm/generated/models/Dicom.ts b/src/api/pfdcm/generated/models/Dicom.ts new file mode 100644 index 000000000..8e1c840ae --- /dev/null +++ b/src/api/pfdcm/generated/models/Dicom.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Not actually a DICOM, just some JSON. + * @export + * @interface Dicom + */ +export interface Dicom { + /** + * A longer sentence about nothing much + * @type {string} + * @memberof Dicom + */ + aCoolPicture?: string; +} + +/** + * Check if a given object implements the Dicom interface. + */ +export function instanceOfDicom(value: object): value is Dicom { + return true; +} + +export function DicomFromJSON(json: any): Dicom { + return DicomFromJSONTyped(json, false); +} + +export function DicomFromJSONTyped(json: any, ignoreDiscriminator: boolean): Dicom { + if (json == null) { + return json; + } + return { + + 'aCoolPicture': json['a_cool_picture'] == null ? undefined : json['a_cool_picture'], + }; +} + +export function DicomToJSON(value?: Dicom | null): any { + if (value == null) { + return value; + } + return { + + 'a_cool_picture': value['aCoolPicture'], + }; +} + diff --git a/src/api/pfdcm/generated/models/EchoModel.ts b/src/api/pfdcm/generated/models/EchoModel.ts new file mode 100644 index 000000000..78c2f39d1 --- /dev/null +++ b/src/api/pfdcm/generated/models/EchoModel.ts @@ -0,0 +1,61 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Simply echo back whatever is POSTed to this API endpoing + * @export + * @interface EchoModel + */ +export interface EchoModel { + /** + * + * @type {string} + * @memberof EchoModel + */ + msg: string; +} + +/** + * Check if a given object implements the EchoModel interface. + */ +export function instanceOfEchoModel(value: object): value is EchoModel { + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function EchoModelFromJSON(json: any): EchoModel { + return EchoModelFromJSONTyped(json, false); +} + +export function EchoModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): EchoModel { + if (json == null) { + return json; + } + return { + + 'msg': json['msg'], + }; +} + +export function EchoModelToJSON(value?: EchoModel | null): any { + if (value == null) { + return value; + } + return { + + 'msg': value['msg'], + }; +} + diff --git a/src/api/pfdcm/generated/models/HTTPValidationError.ts b/src/api/pfdcm/generated/models/HTTPValidationError.ts new file mode 100644 index 000000000..3547980fc --- /dev/null +++ b/src/api/pfdcm/generated/models/HTTPValidationError.ts @@ -0,0 +1,67 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ValidationError } from './ValidationError'; +import { + ValidationErrorFromJSON, + ValidationErrorFromJSONTyped, + ValidationErrorToJSON, +} from './ValidationError'; + +/** + * + * @export + * @interface HTTPValidationError + */ +export interface HTTPValidationError { + /** + * + * @type {Array} + * @memberof HTTPValidationError + */ + detail?: Array; +} + +/** + * Check if a given object implements the HTTPValidationError interface. + */ +export function instanceOfHTTPValidationError(value: object): value is HTTPValidationError { + return true; +} + +export function HTTPValidationErrorFromJSON(json: any): HTTPValidationError { + return HTTPValidationErrorFromJSONTyped(json, false); +} + +export function HTTPValidationErrorFromJSONTyped(json: any, ignoreDiscriminator: boolean): HTTPValidationError { + if (json == null) { + return json; + } + return { + + 'detail': json['detail'] == null ? undefined : ((json['detail'] as Array).map(ValidationErrorFromJSON)), + }; +} + +export function HTTPValidationErrorToJSON(value?: HTTPValidationError | null): any { + if (value == null) { + return value; + } + return { + + 'detail': value['detail'] == null ? undefined : ((value['detail'] as Array).map(ValidationErrorToJSON)), + }; +} + diff --git a/src/api/pfdcm/generated/models/HelloModel.ts b/src/api/pfdcm/generated/models/HelloModel.ts new file mode 100644 index 000000000..b3af2d260 --- /dev/null +++ b/src/api/pfdcm/generated/models/HelloModel.ts @@ -0,0 +1,97 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { SysInfoModel } from './SysInfoModel'; +import { + SysInfoModelFromJSON, + SysInfoModelFromJSONTyped, + SysInfoModelToJSON, +} from './SysInfoModel'; +import type { EchoModel } from './EchoModel'; +import { + EchoModelFromJSON, + EchoModelFromJSONTyped, + EchoModelToJSON, +} from './EchoModel'; + +/** + * The model describing the relevant "hello" data + * @export + * @interface HelloModel + */ +export interface HelloModel { + /** + * + * @type {string} + * @memberof HelloModel + */ + name?: string; + /** + * + * @type {string} + * @memberof HelloModel + */ + version?: string; + /** + * + * @type {SysInfoModel} + * @memberof HelloModel + */ + sysinfo?: SysInfoModel; + /** + * + * @type {EchoModel} + * @memberof HelloModel + */ + echoBack?: EchoModel; +} + +/** + * Check if a given object implements the HelloModel interface. + */ +export function instanceOfHelloModel(value: object): value is HelloModel { + return true; +} + +export function HelloModelFromJSON(json: any): HelloModel { + return HelloModelFromJSONTyped(json, false); +} + +export function HelloModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): HelloModel { + if (json == null) { + return json; + } + return { + + 'name': json['name'] == null ? undefined : json['name'], + 'version': json['version'] == null ? undefined : json['version'], + 'sysinfo': json['sysinfo'] == null ? undefined : SysInfoModelFromJSON(json['sysinfo']), + 'echoBack': json['echoBack'] == null ? undefined : EchoModelFromJSON(json['echoBack']), + }; +} + +export function HelloModelToJSON(value?: HelloModel | null): any { + if (value == null) { + return value; + } + return { + + 'name': value['name'], + 'version': value['version'], + 'sysinfo': SysInfoModelToJSON(value['sysinfo']), + 'echoBack': EchoModelToJSON(value['echoBack']), + }; +} + diff --git a/src/api/pfdcm/generated/models/ListenerDBreturnModel.ts b/src/api/pfdcm/generated/models/ListenerDBreturnModel.ts new file mode 100644 index 000000000..dd2ed6429 --- /dev/null +++ b/src/api/pfdcm/generated/models/ListenerDBreturnModel.ts @@ -0,0 +1,83 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { XinetdDBReturnModel } from './XinetdDBReturnModel'; +import { + XinetdDBReturnModelFromJSON, + XinetdDBReturnModelFromJSONTyped, + XinetdDBReturnModelToJSON, +} from './XinetdDBReturnModel'; +import type { DcmtkDBReturnModel } from './DcmtkDBReturnModel'; +import { + DcmtkDBReturnModelFromJSON, + DcmtkDBReturnModelFromJSONTyped, + DcmtkDBReturnModelToJSON, +} from './DcmtkDBReturnModel'; + +/** + * A full model that is returned from a call to the DB + * @export + * @interface ListenerDBreturnModel + */ +export interface ListenerDBreturnModel { + /** + * + * @type {XinetdDBReturnModel} + * @memberof ListenerDBreturnModel + */ + xinetd: XinetdDBReturnModel; + /** + * + * @type {DcmtkDBReturnModel} + * @memberof ListenerDBreturnModel + */ + dcmtk: DcmtkDBReturnModel; +} + +/** + * Check if a given object implements the ListenerDBreturnModel interface. + */ +export function instanceOfListenerDBreturnModel(value: object): value is ListenerDBreturnModel { + if (!('xinetd' in value) || value['xinetd'] === undefined) return false; + if (!('dcmtk' in value) || value['dcmtk'] === undefined) return false; + return true; +} + +export function ListenerDBreturnModelFromJSON(json: any): ListenerDBreturnModel { + return ListenerDBreturnModelFromJSONTyped(json, false); +} + +export function ListenerDBreturnModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListenerDBreturnModel { + if (json == null) { + return json; + } + return { + + 'xinetd': XinetdDBReturnModelFromJSON(json['xinetd']), + 'dcmtk': DcmtkDBReturnModelFromJSON(json['dcmtk']), + }; +} + +export function ListenerDBreturnModelToJSON(value?: ListenerDBreturnModel | null): any { + if (value == null) { + return value; + } + return { + + 'xinetd': XinetdDBReturnModelToJSON(value['xinetd']), + 'dcmtk': DcmtkDBReturnModelToJSON(value['dcmtk']), + }; +} + diff --git a/src/api/pfdcm/generated/models/ListenerHandlerStatus.ts b/src/api/pfdcm/generated/models/ListenerHandlerStatus.ts new file mode 100644 index 000000000..5e5e5d5d0 --- /dev/null +++ b/src/api/pfdcm/generated/models/ListenerHandlerStatus.ts @@ -0,0 +1,61 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ListenerHandlerStatus + */ +export interface ListenerHandlerStatus { + /** + * + * @type {boolean} + * @memberof ListenerHandlerStatus + */ + status: boolean; +} + +/** + * Check if a given object implements the ListenerHandlerStatus interface. + */ +export function instanceOfListenerHandlerStatus(value: object): value is ListenerHandlerStatus { + if (!('status' in value) || value['status'] === undefined) return false; + return true; +} + +export function ListenerHandlerStatusFromJSON(json: any): ListenerHandlerStatus { + return ListenerHandlerStatusFromJSONTyped(json, false); +} + +export function ListenerHandlerStatusFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListenerHandlerStatus { + if (json == null) { + return json; + } + return { + + 'status': json['status'], + }; +} + +export function ListenerHandlerStatusToJSON(value?: ListenerHandlerStatus | null): any { + if (value == null) { + return value; + } + return { + + 'status': value['status'], + }; +} + diff --git a/src/api/pfdcm/generated/models/LocationInner.ts b/src/api/pfdcm/generated/models/LocationInner.ts new file mode 100644 index 000000000..769423a85 --- /dev/null +++ b/src/api/pfdcm/generated/models/LocationInner.ts @@ -0,0 +1,42 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface LocationInner + */ +export interface LocationInner { +} + +/** + * Check if a given object implements the LocationInner interface. + */ +export function instanceOfLocationInner(value: object): value is LocationInner { + return true; +} + +export function LocationInnerFromJSON(json: any): LocationInner { + return LocationInnerFromJSONTyped(json, false); +} + +export function LocationInnerFromJSONTyped(json: any, ignoreDiscriminator: boolean): LocationInner { + return json; +} + +export function LocationInnerToJSON(value?: LocationInner | null): any { + return value; +} + diff --git a/src/api/pfdcm/generated/models/ModelsListenerModelTime.ts b/src/api/pfdcm/generated/models/ModelsListenerModelTime.ts new file mode 100644 index 000000000..65d0d5d87 --- /dev/null +++ b/src/api/pfdcm/generated/models/ModelsListenerModelTime.ts @@ -0,0 +1,61 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * A simple model that has a time string field + * @export + * @interface ModelsListenerModelTime + */ +export interface ModelsListenerModelTime { + /** + * + * @type {string} + * @memberof ModelsListenerModelTime + */ + time: string; +} + +/** + * Check if a given object implements the ModelsListenerModelTime interface. + */ +export function instanceOfModelsListenerModelTime(value: object): value is ModelsListenerModelTime { + if (!('time' in value) || value['time'] === undefined) return false; + return true; +} + +export function ModelsListenerModelTimeFromJSON(json: any): ModelsListenerModelTime { + return ModelsListenerModelTimeFromJSONTyped(json, false); +} + +export function ModelsListenerModelTimeFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelsListenerModelTime { + if (json == null) { + return json; + } + return { + + 'time': json['time'], + }; +} + +export function ModelsListenerModelTimeToJSON(value?: ModelsListenerModelTime | null): any { + if (value == null) { + return value; + } + return { + + 'time': value['time'], + }; +} + diff --git a/src/api/pfdcm/generated/models/ModelsListenerModelValueStr.ts b/src/api/pfdcm/generated/models/ModelsListenerModelValueStr.ts new file mode 100644 index 000000000..38aa2a83a --- /dev/null +++ b/src/api/pfdcm/generated/models/ModelsListenerModelValueStr.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelsListenerModelValueStr + */ +export interface ModelsListenerModelValueStr { + /** + * + * @type {string} + * @memberof ModelsListenerModelValueStr + */ + value?: string; +} + +/** + * Check if a given object implements the ModelsListenerModelValueStr interface. + */ +export function instanceOfModelsListenerModelValueStr(value: object): value is ModelsListenerModelValueStr { + return true; +} + +export function ModelsListenerModelValueStrFromJSON(json: any): ModelsListenerModelValueStr { + return ModelsListenerModelValueStrFromJSONTyped(json, false); +} + +export function ModelsListenerModelValueStrFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelsListenerModelValueStr { + if (json == null) { + return json; + } + return { + + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function ModelsListenerModelValueStrToJSON(value?: ModelsListenerModelValueStr | null): any { + if (value == null) { + return value; + } + return { + + 'value': value['value'], + }; +} + diff --git a/src/api/pfdcm/generated/models/ModelsPacsQRmodelValueStr.ts b/src/api/pfdcm/generated/models/ModelsPacsQRmodelValueStr.ts new file mode 100644 index 000000000..19f0bb8a9 --- /dev/null +++ b/src/api/pfdcm/generated/models/ModelsPacsQRmodelValueStr.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelsPacsQRmodelValueStr + */ +export interface ModelsPacsQRmodelValueStr { + /** + * + * @type {string} + * @memberof ModelsPacsQRmodelValueStr + */ + value?: string; +} + +/** + * Check if a given object implements the ModelsPacsQRmodelValueStr interface. + */ +export function instanceOfModelsPacsQRmodelValueStr(value: object): value is ModelsPacsQRmodelValueStr { + return true; +} + +export function ModelsPacsQRmodelValueStrFromJSON(json: any): ModelsPacsQRmodelValueStr { + return ModelsPacsQRmodelValueStrFromJSONTyped(json, false); +} + +export function ModelsPacsQRmodelValueStrFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelsPacsQRmodelValueStr { + if (json == null) { + return json; + } + return { + + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function ModelsPacsQRmodelValueStrToJSON(value?: ModelsPacsQRmodelValueStr | null): any { + if (value == null) { + return value; + } + return { + + 'value': value['value'], + }; +} + diff --git a/src/api/pfdcm/generated/models/ModelsPacsSetupModelTime.ts b/src/api/pfdcm/generated/models/ModelsPacsSetupModelTime.ts new file mode 100644 index 000000000..ab5e686aa --- /dev/null +++ b/src/api/pfdcm/generated/models/ModelsPacsSetupModelTime.ts @@ -0,0 +1,61 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * A simple model that has a time string field + * @export + * @interface ModelsPacsSetupModelTime + */ +export interface ModelsPacsSetupModelTime { + /** + * + * @type {string} + * @memberof ModelsPacsSetupModelTime + */ + time: string; +} + +/** + * Check if a given object implements the ModelsPacsSetupModelTime interface. + */ +export function instanceOfModelsPacsSetupModelTime(value: object): value is ModelsPacsSetupModelTime { + if (!('time' in value) || value['time'] === undefined) return false; + return true; +} + +export function ModelsPacsSetupModelTimeFromJSON(json: any): ModelsPacsSetupModelTime { + return ModelsPacsSetupModelTimeFromJSONTyped(json, false); +} + +export function ModelsPacsSetupModelTimeFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelsPacsSetupModelTime { + if (json == null) { + return json; + } + return { + + 'time': json['time'], + }; +} + +export function ModelsPacsSetupModelTimeToJSON(value?: ModelsPacsSetupModelTime | null): any { + if (value == null) { + return value; + } + return { + + 'time': value['time'], + }; +} + diff --git a/src/api/pfdcm/generated/models/ModelsPacsSetupModelValueStr.ts b/src/api/pfdcm/generated/models/ModelsPacsSetupModelValueStr.ts new file mode 100644 index 000000000..ba3f1ff20 --- /dev/null +++ b/src/api/pfdcm/generated/models/ModelsPacsSetupModelValueStr.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelsPacsSetupModelValueStr + */ +export interface ModelsPacsSetupModelValueStr { + /** + * + * @type {string} + * @memberof ModelsPacsSetupModelValueStr + */ + value?: string; +} + +/** + * Check if a given object implements the ModelsPacsSetupModelValueStr interface. + */ +export function instanceOfModelsPacsSetupModelValueStr(value: object): value is ModelsPacsSetupModelValueStr { + return true; +} + +export function ModelsPacsSetupModelValueStrFromJSON(json: any): ModelsPacsSetupModelValueStr { + return ModelsPacsSetupModelValueStrFromJSONTyped(json, false); +} + +export function ModelsPacsSetupModelValueStrFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelsPacsSetupModelValueStr { + if (json == null) { + return json; + } + return { + + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function ModelsPacsSetupModelValueStrToJSON(value?: ModelsPacsSetupModelValueStr | null): any { + if (value == null) { + return value; + } + return { + + 'value': value['value'], + }; +} + diff --git a/src/api/pfdcm/generated/models/ModelsSmdbSetupModelValueStr.ts b/src/api/pfdcm/generated/models/ModelsSmdbSetupModelValueStr.ts new file mode 100644 index 000000000..169f10f83 --- /dev/null +++ b/src/api/pfdcm/generated/models/ModelsSmdbSetupModelValueStr.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelsSmdbSetupModelValueStr + */ +export interface ModelsSmdbSetupModelValueStr { + /** + * + * @type {string} + * @memberof ModelsSmdbSetupModelValueStr + */ + value?: string; +} + +/** + * Check if a given object implements the ModelsSmdbSetupModelValueStr interface. + */ +export function instanceOfModelsSmdbSetupModelValueStr(value: object): value is ModelsSmdbSetupModelValueStr { + return true; +} + +export function ModelsSmdbSetupModelValueStrFromJSON(json: any): ModelsSmdbSetupModelValueStr { + return ModelsSmdbSetupModelValueStrFromJSONTyped(json, false); +} + +export function ModelsSmdbSetupModelValueStrFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelsSmdbSetupModelValueStr { + if (json == null) { + return json; + } + return { + + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function ModelsSmdbSetupModelValueStrToJSON(value?: ModelsSmdbSetupModelValueStr | null): any { + if (value == null) { + return value; + } + return { + + 'value': value['value'], + }; +} + diff --git a/src/api/pfdcm/generated/models/PACSasync.ts b/src/api/pfdcm/generated/models/PACSasync.ts new file mode 100644 index 000000000..ba5a89467 --- /dev/null +++ b/src/api/pfdcm/generated/models/PACSasync.ts @@ -0,0 +1,87 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * A model returned when an async PACS directive is indicated + * @export + * @interface PACSasync + */ +export interface PACSasync { + /** + * + * @type {string} + * @memberof PACSasync + */ + directiveType?: string; + /** + * + * @type {object} + * @memberof PACSasync + */ + response: object; + /** + * + * @type {string} + * @memberof PACSasync + */ + timestamp: string; + /** + * + * @type {object} + * @memberof PACSasync + */ + pACSdirective: object; +} + +/** + * Check if a given object implements the PACSasync interface. + */ +export function instanceOfPACSasync(value: object): value is PACSasync { + if (!('response' in value) || value['response'] === undefined) return false; + if (!('timestamp' in value) || value['timestamp'] === undefined) return false; + if (!('pACSdirective' in value) || value['pACSdirective'] === undefined) return false; + return true; +} + +export function PACSasyncFromJSON(json: any): PACSasync { + return PACSasyncFromJSONTyped(json, false); +} + +export function PACSasyncFromJSONTyped(json: any, ignoreDiscriminator: boolean): PACSasync { + if (json == null) { + return json; + } + return { + + 'directiveType': json['directiveType'] == null ? undefined : json['directiveType'], + 'response': json['response'], + 'timestamp': json['timestamp'], + 'pACSdirective': json['PACSdirective'], + }; +} + +export function PACSasyncToJSON(value?: PACSasync | null): any { + if (value == null) { + return value; + } + return { + + 'directiveType': value['directiveType'], + 'response': value['response'], + 'timestamp': value['timestamp'], + 'PACSdirective': value['pACSdirective'], + }; +} + diff --git a/src/api/pfdcm/generated/models/PACSdbPutModel.ts b/src/api/pfdcm/generated/models/PACSdbPutModel.ts new file mode 100644 index 000000000..27b7d39d0 --- /dev/null +++ b/src/api/pfdcm/generated/models/PACSdbPutModel.ts @@ -0,0 +1,68 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { PACSsetupCore } from './PACSsetupCore'; +import { + PACSsetupCoreFromJSON, + PACSsetupCoreFromJSONTyped, + PACSsetupCoreToJSON, +} from './PACSsetupCore'; + +/** + * Model that illustrates what to PUT to the DB + * @export + * @interface PACSdbPutModel + */ +export interface PACSdbPutModel { + /** + * + * @type {PACSsetupCore} + * @memberof PACSdbPutModel + */ + info: PACSsetupCore; +} + +/** + * Check if a given object implements the PACSdbPutModel interface. + */ +export function instanceOfPACSdbPutModel(value: object): value is PACSdbPutModel { + if (!('info' in value) || value['info'] === undefined) return false; + return true; +} + +export function PACSdbPutModelFromJSON(json: any): PACSdbPutModel { + return PACSdbPutModelFromJSONTyped(json, false); +} + +export function PACSdbPutModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): PACSdbPutModel { + if (json == null) { + return json; + } + return { + + 'info': PACSsetupCoreFromJSON(json['info']), + }; +} + +export function PACSdbPutModelToJSON(value?: PACSdbPutModel | null): any { + if (value == null) { + return value; + } + return { + + 'info': PACSsetupCoreToJSON(value['info']), + }; +} + diff --git a/src/api/pfdcm/generated/models/PACSdbReturnModel.ts b/src/api/pfdcm/generated/models/PACSdbReturnModel.ts new file mode 100644 index 000000000..3e3b9de65 --- /dev/null +++ b/src/api/pfdcm/generated/models/PACSdbReturnModel.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsPacsSetupModelTime } from './ModelsPacsSetupModelTime'; +import { + ModelsPacsSetupModelTimeFromJSON, + ModelsPacsSetupModelTimeFromJSONTyped, + ModelsPacsSetupModelTimeToJSON, +} from './ModelsPacsSetupModelTime'; +import type { PACSsetupCore } from './PACSsetupCore'; +import { + PACSsetupCoreFromJSON, + PACSsetupCoreFromJSONTyped, + PACSsetupCoreToJSON, +} from './PACSsetupCore'; + +/** + * A full model that is returned from a call to the DB + * @export + * @interface PACSdbReturnModel + */ +export interface PACSdbReturnModel { + /** + * + * @type {PACSsetupCore} + * @memberof PACSdbReturnModel + */ + info: PACSsetupCore; + /** + * + * @type {ModelsPacsSetupModelTime} + * @memberof PACSdbReturnModel + */ + timeCreated: ModelsPacsSetupModelTime; + /** + * + * @type {ModelsPacsSetupModelTime} + * @memberof PACSdbReturnModel + */ + timeModified: ModelsPacsSetupModelTime; + /** + * + * @type {string} + * @memberof PACSdbReturnModel + */ + message: string; +} + +/** + * Check if a given object implements the PACSdbReturnModel interface. + */ +export function instanceOfPACSdbReturnModel(value: object): value is PACSdbReturnModel { + if (!('info' in value) || value['info'] === undefined) return false; + if (!('timeCreated' in value) || value['timeCreated'] === undefined) return false; + if (!('timeModified' in value) || value['timeModified'] === undefined) return false; + if (!('message' in value) || value['message'] === undefined) return false; + return true; +} + +export function PACSdbReturnModelFromJSON(json: any): PACSdbReturnModel { + return PACSdbReturnModelFromJSONTyped(json, false); +} + +export function PACSdbReturnModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): PACSdbReturnModel { + if (json == null) { + return json; + } + return { + + 'info': PACSsetupCoreFromJSON(json['info']), + 'timeCreated': ModelsPacsSetupModelTimeFromJSON(json['time_created']), + 'timeModified': ModelsPacsSetupModelTimeFromJSON(json['time_modified']), + 'message': json['message'], + }; +} + +export function PACSdbReturnModelToJSON(value?: PACSdbReturnModel | null): any { + if (value == null) { + return value; + } + return { + + 'info': PACSsetupCoreToJSON(value['info']), + 'time_created': ModelsPacsSetupModelTimeToJSON(value['timeCreated']), + 'time_modified': ModelsPacsSetupModelTimeToJSON(value['timeModified']), + 'message': value['message'], + }; +} + diff --git a/src/api/pfdcm/generated/models/PACSqueryCore.ts b/src/api/pfdcm/generated/models/PACSqueryCore.ts new file mode 100644 index 000000000..aa894839f --- /dev/null +++ b/src/api/pfdcm/generated/models/PACSqueryCore.ts @@ -0,0 +1,252 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The PACS Query model + * @export + * @interface PACSqueryCore + */ +export interface PACSqueryCore { + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + accessionNumber?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + patientID?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + patientName?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + patientBirthDate?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + patientAge?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + patientSex?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + studyDate?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + studyDescription?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + studyInstanceUID?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + modality?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + modalitiesInStudy?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + performedStationAETitle?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + numberOfSeriesRelatedInstances?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + instanceNumber?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + seriesDate?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + seriesDescription?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + seriesInstanceUID?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + protocolName?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + acquisitionProtocolDescription?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + acquisitionProtocolName?: string; + /** + * + * @type {boolean} + * @memberof PACSqueryCore + */ + withFeedBack?: boolean; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + then?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + thenArgs?: string; + /** + * + * @type {string} + * @memberof PACSqueryCore + */ + dblogbasepath?: string; + /** + * + * @type {boolean} + * @memberof PACSqueryCore + */ + jsonResponse?: boolean; +} + +/** + * Check if a given object implements the PACSqueryCore interface. + */ +export function instanceOfPACSqueryCore(value: object): value is PACSqueryCore { + return true; +} + +export function PACSqueryCoreFromJSON(json: any): PACSqueryCore { + return PACSqueryCoreFromJSONTyped(json, false); +} + +export function PACSqueryCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): PACSqueryCore { + if (json == null) { + return json; + } + return { + + 'accessionNumber': json['AccessionNumber'] == null ? undefined : json['AccessionNumber'], + 'patientID': json['PatientID'] == null ? undefined : json['PatientID'], + 'patientName': json['PatientName'] == null ? undefined : json['PatientName'], + 'patientBirthDate': json['PatientBirthDate'] == null ? undefined : json['PatientBirthDate'], + 'patientAge': json['PatientAge'] == null ? undefined : json['PatientAge'], + 'patientSex': json['PatientSex'] == null ? undefined : json['PatientSex'], + 'studyDate': json['StudyDate'] == null ? undefined : json['StudyDate'], + 'studyDescription': json['StudyDescription'] == null ? undefined : json['StudyDescription'], + 'studyInstanceUID': json['StudyInstanceUID'] == null ? undefined : json['StudyInstanceUID'], + 'modality': json['Modality'] == null ? undefined : json['Modality'], + 'modalitiesInStudy': json['ModalitiesInStudy'] == null ? undefined : json['ModalitiesInStudy'], + 'performedStationAETitle': json['PerformedStationAETitle'] == null ? undefined : json['PerformedStationAETitle'], + 'numberOfSeriesRelatedInstances': json['NumberOfSeriesRelatedInstances'] == null ? undefined : json['NumberOfSeriesRelatedInstances'], + 'instanceNumber': json['InstanceNumber'] == null ? undefined : json['InstanceNumber'], + 'seriesDate': json['SeriesDate'] == null ? undefined : json['SeriesDate'], + 'seriesDescription': json['SeriesDescription'] == null ? undefined : json['SeriesDescription'], + 'seriesInstanceUID': json['SeriesInstanceUID'] == null ? undefined : json['SeriesInstanceUID'], + 'protocolName': json['ProtocolName'] == null ? undefined : json['ProtocolName'], + 'acquisitionProtocolDescription': json['AcquisitionProtocolDescription'] == null ? undefined : json['AcquisitionProtocolDescription'], + 'acquisitionProtocolName': json['AcquisitionProtocolName'] == null ? undefined : json['AcquisitionProtocolName'], + 'withFeedBack': json['withFeedBack'] == null ? undefined : json['withFeedBack'], + 'then': json['then'] == null ? undefined : json['then'], + 'thenArgs': json['thenArgs'] == null ? undefined : json['thenArgs'], + 'dblogbasepath': json['dblogbasepath'] == null ? undefined : json['dblogbasepath'], + 'jsonResponse': json['json_response'] == null ? undefined : json['json_response'], + }; +} + +export function PACSqueryCoreToJSON(value?: PACSqueryCore | null): any { + if (value == null) { + return value; + } + return { + + 'AccessionNumber': value['accessionNumber'], + 'PatientID': value['patientID'], + 'PatientName': value['patientName'], + 'PatientBirthDate': value['patientBirthDate'], + 'PatientAge': value['patientAge'], + 'PatientSex': value['patientSex'], + 'StudyDate': value['studyDate'], + 'StudyDescription': value['studyDescription'], + 'StudyInstanceUID': value['studyInstanceUID'], + 'Modality': value['modality'], + 'ModalitiesInStudy': value['modalitiesInStudy'], + 'PerformedStationAETitle': value['performedStationAETitle'], + 'NumberOfSeriesRelatedInstances': value['numberOfSeriesRelatedInstances'], + 'InstanceNumber': value['instanceNumber'], + 'SeriesDate': value['seriesDate'], + 'SeriesDescription': value['seriesDescription'], + 'SeriesInstanceUID': value['seriesInstanceUID'], + 'ProtocolName': value['protocolName'], + 'AcquisitionProtocolDescription': value['acquisitionProtocolDescription'], + 'AcquisitionProtocolName': value['acquisitionProtocolName'], + 'withFeedBack': value['withFeedBack'], + 'then': value['then'], + 'thenArgs': value['thenArgs'], + 'dblogbasepath': value['dblogbasepath'], + 'json_response': value['jsonResponse'], + }; +} + diff --git a/src/api/pfdcm/generated/models/PACSsetupCore.ts b/src/api/pfdcm/generated/models/PACSsetupCore.ts new file mode 100644 index 000000000..60f529b21 --- /dev/null +++ b/src/api/pfdcm/generated/models/PACSsetupCore.ts @@ -0,0 +1,92 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The PACS service model + * @export + * @interface PACSsetupCore + */ +export interface PACSsetupCore { + /** + * + * @type {string} + * @memberof PACSsetupCore + */ + aet?: string; + /** + * + * @type {string} + * @memberof PACSsetupCore + */ + aetListener?: string; + /** + * + * @type {string} + * @memberof PACSsetupCore + */ + aec?: string; + /** + * + * @type {string} + * @memberof PACSsetupCore + */ + serverIP?: string; + /** + * + * @type {string} + * @memberof PACSsetupCore + */ + serverPort?: string; +} + +/** + * Check if a given object implements the PACSsetupCore interface. + */ +export function instanceOfPACSsetupCore(value: object): value is PACSsetupCore { + return true; +} + +export function PACSsetupCoreFromJSON(json: any): PACSsetupCore { + return PACSsetupCoreFromJSONTyped(json, false); +} + +export function PACSsetupCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): PACSsetupCore { + if (json == null) { + return json; + } + return { + + 'aet': json['aet'] == null ? undefined : json['aet'], + 'aetListener': json['aet_listener'] == null ? undefined : json['aet_listener'], + 'aec': json['aec'] == null ? undefined : json['aec'], + 'serverIP': json['serverIP'] == null ? undefined : json['serverIP'], + 'serverPort': json['serverPort'] == null ? undefined : json['serverPort'], + }; +} + +export function PACSsetupCoreToJSON(value?: PACSsetupCore | null): any { + if (value == null) { + return value; + } + return { + + 'aet': value['aet'], + 'aet_listener': value['aetListener'], + 'aec': value['aec'], + 'serverIP': value['serverIP'], + 'serverPort': value['serverPort'], + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBFsConfig.ts b/src/api/pfdcm/generated/models/SMDBFsConfig.ts new file mode 100644 index 000000000..fe2f0aab1 --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBFsConfig.ts @@ -0,0 +1,83 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsSmdbSetupModelValueStr } from './ModelsSmdbSetupModelValueStr'; +import { + ModelsSmdbSetupModelValueStrFromJSON, + ModelsSmdbSetupModelValueStrFromJSONTyped, + ModelsSmdbSetupModelValueStrToJSON, +} from './ModelsSmdbSetupModelValueStr'; +import type { SMDBFsCore } from './SMDBFsCore'; +import { + SMDBFsCoreFromJSON, + SMDBFsCoreFromJSONTyped, + SMDBFsCoreToJSON, +} from './SMDBFsCore'; + +/** + * The SMDB FS key config model + * @export + * @interface SMDBFsConfig + */ +export interface SMDBFsConfig { + /** + * + * @type {ModelsSmdbSetupModelValueStr} + * @memberof SMDBFsConfig + */ + fsKeyName: ModelsSmdbSetupModelValueStr; + /** + * + * @type {SMDBFsCore} + * @memberof SMDBFsConfig + */ + fsInfo: SMDBFsCore; +} + +/** + * Check if a given object implements the SMDBFsConfig interface. + */ +export function instanceOfSMDBFsConfig(value: object): value is SMDBFsConfig { + if (!('fsKeyName' in value) || value['fsKeyName'] === undefined) return false; + if (!('fsInfo' in value) || value['fsInfo'] === undefined) return false; + return true; +} + +export function SMDBFsConfigFromJSON(json: any): SMDBFsConfig { + return SMDBFsConfigFromJSONTyped(json, false); +} + +export function SMDBFsConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBFsConfig { + if (json == null) { + return json; + } + return { + + 'fsKeyName': ModelsSmdbSetupModelValueStrFromJSON(json['fsKeyName']), + 'fsInfo': SMDBFsCoreFromJSON(json['fsInfo']), + }; +} + +export function SMDBFsConfigToJSON(value?: SMDBFsConfig | null): any { + if (value == null) { + return value; + } + return { + + 'fsKeyName': ModelsSmdbSetupModelValueStrToJSON(value['fsKeyName']), + 'fsInfo': SMDBFsCoreToJSON(value['fsInfo']), + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBFsCore.ts b/src/api/pfdcm/generated/models/SMDBFsCore.ts new file mode 100644 index 000000000..485db73db --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBFsCore.ts @@ -0,0 +1,61 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The SMDB file system service model + * @export + * @interface SMDBFsCore + */ +export interface SMDBFsCore { + /** + * + * @type {string} + * @memberof SMDBFsCore + */ + storepath: string; +} + +/** + * Check if a given object implements the SMDBFsCore interface. + */ +export function instanceOfSMDBFsCore(value: object): value is SMDBFsCore { + if (!('storepath' in value) || value['storepath'] === undefined) return false; + return true; +} + +export function SMDBFsCoreFromJSON(json: any): SMDBFsCore { + return SMDBFsCoreFromJSONTyped(json, false); +} + +export function SMDBFsCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBFsCore { + if (json == null) { + return json; + } + return { + + 'storepath': json['storepath'], + }; +} + +export function SMDBFsCoreToJSON(value?: SMDBFsCore | null): any { + if (value == null) { + return value; + } + return { + + 'storepath': value['storepath'], + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBFsReturnModel.ts b/src/api/pfdcm/generated/models/SMDBFsReturnModel.ts new file mode 100644 index 000000000..4e248dca8 --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBFsReturnModel.ts @@ -0,0 +1,85 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { SMDBFsCore } from './SMDBFsCore'; +import { + SMDBFsCoreFromJSON, + SMDBFsCoreFromJSONTyped, + SMDBFsCoreToJSON, +} from './SMDBFsCore'; + +/** + * A full model that is returned from a call to the DB + * @export + * @interface SMDBFsReturnModel + */ +export interface SMDBFsReturnModel { + /** + * + * @type {boolean} + * @memberof SMDBFsReturnModel + */ + status?: boolean; + /** + * + * @type {string} + * @memberof SMDBFsReturnModel + */ + fsKeyName: string; + /** + * + * @type {SMDBFsCore} + * @memberof SMDBFsReturnModel + */ + fsInfo: SMDBFsCore; +} + +/** + * Check if a given object implements the SMDBFsReturnModel interface. + */ +export function instanceOfSMDBFsReturnModel(value: object): value is SMDBFsReturnModel { + if (!('fsKeyName' in value) || value['fsKeyName'] === undefined) return false; + if (!('fsInfo' in value) || value['fsInfo'] === undefined) return false; + return true; +} + +export function SMDBFsReturnModelFromJSON(json: any): SMDBFsReturnModel { + return SMDBFsReturnModelFromJSONTyped(json, false); +} + +export function SMDBFsReturnModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBFsReturnModel { + if (json == null) { + return json; + } + return { + + 'status': json['status'] == null ? undefined : json['status'], + 'fsKeyName': json['fsKeyName'], + 'fsInfo': SMDBFsCoreFromJSON(json['fsInfo']), + }; +} + +export function SMDBFsReturnModelToJSON(value?: SMDBFsReturnModel | null): any { + if (value == null) { + return value; + } + return { + + 'status': value['status'], + 'fsKeyName': value['fsKeyName'], + 'fsInfo': SMDBFsCoreToJSON(value['fsInfo']), + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBcubeConfig.ts b/src/api/pfdcm/generated/models/SMDBcubeConfig.ts new file mode 100644 index 000000000..5668d959a --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBcubeConfig.ts @@ -0,0 +1,83 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsSmdbSetupModelValueStr } from './ModelsSmdbSetupModelValueStr'; +import { + ModelsSmdbSetupModelValueStrFromJSON, + ModelsSmdbSetupModelValueStrFromJSONTyped, + ModelsSmdbSetupModelValueStrToJSON, +} from './ModelsSmdbSetupModelValueStr'; +import type { SMDBcubeCore } from './SMDBcubeCore'; +import { + SMDBcubeCoreFromJSON, + SMDBcubeCoreFromJSONTyped, + SMDBcubeCoreToJSON, +} from './SMDBcubeCore'; + +/** + * The SMDB cube key config model + * @export + * @interface SMDBcubeConfig + */ +export interface SMDBcubeConfig { + /** + * + * @type {ModelsSmdbSetupModelValueStr} + * @memberof SMDBcubeConfig + */ + cubeKeyName: ModelsSmdbSetupModelValueStr; + /** + * + * @type {SMDBcubeCore} + * @memberof SMDBcubeConfig + */ + cubeInfo: SMDBcubeCore; +} + +/** + * Check if a given object implements the SMDBcubeConfig interface. + */ +export function instanceOfSMDBcubeConfig(value: object): value is SMDBcubeConfig { + if (!('cubeKeyName' in value) || value['cubeKeyName'] === undefined) return false; + if (!('cubeInfo' in value) || value['cubeInfo'] === undefined) return false; + return true; +} + +export function SMDBcubeConfigFromJSON(json: any): SMDBcubeConfig { + return SMDBcubeConfigFromJSONTyped(json, false); +} + +export function SMDBcubeConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBcubeConfig { + if (json == null) { + return json; + } + return { + + 'cubeKeyName': ModelsSmdbSetupModelValueStrFromJSON(json['cubeKeyName']), + 'cubeInfo': SMDBcubeCoreFromJSON(json['cubeInfo']), + }; +} + +export function SMDBcubeConfigToJSON(value?: SMDBcubeConfig | null): any { + if (value == null) { + return value; + } + return { + + 'cubeKeyName': ModelsSmdbSetupModelValueStrToJSON(value['cubeKeyName']), + 'cubeInfo': SMDBcubeCoreToJSON(value['cubeInfo']), + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBcubeCore.ts b/src/api/pfdcm/generated/models/SMDBcubeCore.ts new file mode 100644 index 000000000..845ebd857 --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBcubeCore.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The SMDB cube service model + * @export + * @interface SMDBcubeCore + */ +export interface SMDBcubeCore { + /** + * + * @type {string} + * @memberof SMDBcubeCore + */ + url: string; + /** + * + * @type {string} + * @memberof SMDBcubeCore + */ + username: string; + /** + * + * @type {string} + * @memberof SMDBcubeCore + */ + password: string; +} + +/** + * Check if a given object implements the SMDBcubeCore interface. + */ +export function instanceOfSMDBcubeCore(value: object): value is SMDBcubeCore { + if (!('url' in value) || value['url'] === undefined) return false; + if (!('username' in value) || value['username'] === undefined) return false; + if (!('password' in value) || value['password'] === undefined) return false; + return true; +} + +export function SMDBcubeCoreFromJSON(json: any): SMDBcubeCore { + return SMDBcubeCoreFromJSONTyped(json, false); +} + +export function SMDBcubeCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBcubeCore { + if (json == null) { + return json; + } + return { + + 'url': json['url'], + 'username': json['username'], + 'password': json['password'], + }; +} + +export function SMDBcubeCoreToJSON(value?: SMDBcubeCore | null): any { + if (value == null) { + return value; + } + return { + + 'url': value['url'], + 'username': value['username'], + 'password': value['password'], + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBcubeReturnModel.ts b/src/api/pfdcm/generated/models/SMDBcubeReturnModel.ts new file mode 100644 index 000000000..a09dc5543 --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBcubeReturnModel.ts @@ -0,0 +1,85 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { SMDBcubeCore } from './SMDBcubeCore'; +import { + SMDBcubeCoreFromJSON, + SMDBcubeCoreFromJSONTyped, + SMDBcubeCoreToJSON, +} from './SMDBcubeCore'; + +/** + * A full model that is returned from a call to the DB + * @export + * @interface SMDBcubeReturnModel + */ +export interface SMDBcubeReturnModel { + /** + * + * @type {boolean} + * @memberof SMDBcubeReturnModel + */ + status?: boolean; + /** + * + * @type {string} + * @memberof SMDBcubeReturnModel + */ + cubeKeyName: string; + /** + * + * @type {SMDBcubeCore} + * @memberof SMDBcubeReturnModel + */ + cubeInfo: SMDBcubeCore; +} + +/** + * Check if a given object implements the SMDBcubeReturnModel interface. + */ +export function instanceOfSMDBcubeReturnModel(value: object): value is SMDBcubeReturnModel { + if (!('cubeKeyName' in value) || value['cubeKeyName'] === undefined) return false; + if (!('cubeInfo' in value) || value['cubeInfo'] === undefined) return false; + return true; +} + +export function SMDBcubeReturnModelFromJSON(json: any): SMDBcubeReturnModel { + return SMDBcubeReturnModelFromJSONTyped(json, false); +} + +export function SMDBcubeReturnModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBcubeReturnModel { + if (json == null) { + return json; + } + return { + + 'status': json['status'] == null ? undefined : json['status'], + 'cubeKeyName': json['cubeKeyName'], + 'cubeInfo': SMDBcubeCoreFromJSON(json['cubeInfo']), + }; +} + +export function SMDBcubeReturnModelToJSON(value?: SMDBcubeReturnModel | null): any { + if (value == null) { + return value; + } + return { + + 'status': value['status'], + 'cubeKeyName': value['cubeKeyName'], + 'cubeInfo': SMDBcubeCoreToJSON(value['cubeInfo']), + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBswiftConfig.ts b/src/api/pfdcm/generated/models/SMDBswiftConfig.ts new file mode 100644 index 000000000..59ee1a641 --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBswiftConfig.ts @@ -0,0 +1,83 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsSmdbSetupModelValueStr } from './ModelsSmdbSetupModelValueStr'; +import { + ModelsSmdbSetupModelValueStrFromJSON, + ModelsSmdbSetupModelValueStrFromJSONTyped, + ModelsSmdbSetupModelValueStrToJSON, +} from './ModelsSmdbSetupModelValueStr'; +import type { SMDBswiftCore } from './SMDBswiftCore'; +import { + SMDBswiftCoreFromJSON, + SMDBswiftCoreFromJSONTyped, + SMDBswiftCoreToJSON, +} from './SMDBswiftCore'; + +/** + * The SMDB swift key config model + * @export + * @interface SMDBswiftConfig + */ +export interface SMDBswiftConfig { + /** + * + * @type {ModelsSmdbSetupModelValueStr} + * @memberof SMDBswiftConfig + */ + swiftKeyName: ModelsSmdbSetupModelValueStr; + /** + * + * @type {SMDBswiftCore} + * @memberof SMDBswiftConfig + */ + swiftInfo: SMDBswiftCore; +} + +/** + * Check if a given object implements the SMDBswiftConfig interface. + */ +export function instanceOfSMDBswiftConfig(value: object): value is SMDBswiftConfig { + if (!('swiftKeyName' in value) || value['swiftKeyName'] === undefined) return false; + if (!('swiftInfo' in value) || value['swiftInfo'] === undefined) return false; + return true; +} + +export function SMDBswiftConfigFromJSON(json: any): SMDBswiftConfig { + return SMDBswiftConfigFromJSONTyped(json, false); +} + +export function SMDBswiftConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBswiftConfig { + if (json == null) { + return json; + } + return { + + 'swiftKeyName': ModelsSmdbSetupModelValueStrFromJSON(json['swiftKeyName']), + 'swiftInfo': SMDBswiftCoreFromJSON(json['swiftInfo']), + }; +} + +export function SMDBswiftConfigToJSON(value?: SMDBswiftConfig | null): any { + if (value == null) { + return value; + } + return { + + 'swiftKeyName': ModelsSmdbSetupModelValueStrToJSON(value['swiftKeyName']), + 'swiftInfo': SMDBswiftCoreToJSON(value['swiftInfo']), + }; +} + diff --git a/src/api/pfdcm/generated/models/SMDBswiftCore.ts b/src/api/pfdcm/generated/models/SMDBswiftCore.ts new file mode 100644 index 000000000..cd8dca4ae --- /dev/null +++ b/src/api/pfdcm/generated/models/SMDBswiftCore.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The SMDB swift service model + * @export + * @interface SMDBswiftCore + */ +export interface SMDBswiftCore { + /** + * + * @type {string} + * @memberof SMDBswiftCore + */ + ip: string; + /** + * + * @type {string} + * @memberof SMDBswiftCore + */ + port: string; + /** + * + * @type {string} + * @memberof SMDBswiftCore + */ + login: string; +} + +/** + * Check if a given object implements the SMDBswiftCore interface. + */ +export function instanceOfSMDBswiftCore(value: object): value is SMDBswiftCore { + if (!('ip' in value) || value['ip'] === undefined) return false; + if (!('port' in value) || value['port'] === undefined) return false; + if (!('login' in value) || value['login'] === undefined) return false; + return true; +} + +export function SMDBswiftCoreFromJSON(json: any): SMDBswiftCore { + return SMDBswiftCoreFromJSONTyped(json, false); +} + +export function SMDBswiftCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): SMDBswiftCore { + if (json == null) { + return json; + } + return { + + 'ip': json['ip'], + 'port': json['port'], + 'login': json['login'], + }; +} + +export function SMDBswiftCoreToJSON(value?: SMDBswiftCore | null): any { + if (value == null) { + return value; + } + return { + + 'ip': value['ip'], + 'port': value['port'], + 'login': value['login'], + }; +} + diff --git a/src/api/pfdcm/generated/models/SysInfoModel.ts b/src/api/pfdcm/generated/models/SysInfoModel.ts new file mode 100644 index 000000000..0411adf6b --- /dev/null +++ b/src/api/pfdcm/generated/models/SysInfoModel.ts @@ -0,0 +1,148 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * For the most part, copied from + * https://github.com/FNNDSC/pfcon/blob/87f5da953be7c2cc80542bef0e67727dda1b4958/pfcon/pfcon.py#L601-611 + * + * Provides information about the environment in which the service + * is currently running. + * @export + * @interface SysInfoModel + */ +export interface SysInfoModel { + /** + * + * @type {string} + * @memberof SysInfoModel + */ + system?: string; + /** + * + * @type {string} + * @memberof SysInfoModel + */ + machine?: string; + /** + * Uname output, converted from object to list + * @type {Array} + * @memberof SysInfoModel + */ + uname?: Array; + /** + * + * @type {string} + * @memberof SysInfoModel + */ + platform?: string; + /** + * + * @type {string} + * @memberof SysInfoModel + */ + version: string; + /** + * Actually a NamedTuple but I'm not typing it out + * @type {Array} + * @memberof SysInfoModel + */ + memory: Array; + /** + * + * @type {number} + * @memberof SysInfoModel + */ + cpucount?: number; + /** + * Average system load over last 1, 5, and 15 minutes + * @type {Array} + * @memberof SysInfoModel + */ + loadavg: Array; + /** + * + * @type {number} + * @memberof SysInfoModel + */ + cpuPercent: number; + /** + * + * @type {string} + * @memberof SysInfoModel + */ + hostname?: string; + /** + * + * @type {string} + * @memberof SysInfoModel + */ + inet?: string; +} + +/** + * Check if a given object implements the SysInfoModel interface. + */ +export function instanceOfSysInfoModel(value: object): value is SysInfoModel { + if (!('version' in value) || value['version'] === undefined) return false; + if (!('memory' in value) || value['memory'] === undefined) return false; + if (!('loadavg' in value) || value['loadavg'] === undefined) return false; + if (!('cpuPercent' in value) || value['cpuPercent'] === undefined) return false; + return true; +} + +export function SysInfoModelFromJSON(json: any): SysInfoModel { + return SysInfoModelFromJSONTyped(json, false); +} + +export function SysInfoModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): SysInfoModel { + if (json == null) { + return json; + } + return { + + 'system': json['system'] == null ? undefined : json['system'], + 'machine': json['machine'] == null ? undefined : json['machine'], + 'uname': json['uname'] == null ? undefined : json['uname'], + 'platform': json['platform'] == null ? undefined : json['platform'], + 'version': json['version'], + 'memory': json['memory'], + 'cpucount': json['cpucount'] == null ? undefined : json['cpucount'], + 'loadavg': json['loadavg'], + 'cpuPercent': json['cpu_percent'], + 'hostname': json['hostname'] == null ? undefined : json['hostname'], + 'inet': json['inet'] == null ? undefined : json['inet'], + }; +} + +export function SysInfoModelToJSON(value?: SysInfoModel | null): any { + if (value == null) { + return value; + } + return { + + 'system': value['system'], + 'machine': value['machine'], + 'uname': value['uname'], + 'platform': value['platform'], + 'version': value['version'], + 'memory': value['memory'], + 'cpucount': value['cpucount'], + 'loadavg': value['loadavg'], + 'cpu_percent': value['cpuPercent'], + 'hostname': value['hostname'], + 'inet': value['inet'], + }; +} + diff --git a/src/api/pfdcm/generated/models/ValidationError.ts b/src/api/pfdcm/generated/models/ValidationError.ts new file mode 100644 index 000000000..2355a36ea --- /dev/null +++ b/src/api/pfdcm/generated/models/ValidationError.ts @@ -0,0 +1,86 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { LocationInner } from './LocationInner'; +import { + LocationInnerFromJSON, + LocationInnerFromJSONTyped, + LocationInnerToJSON, +} from './LocationInner'; + +/** + * + * @export + * @interface ValidationError + */ +export interface ValidationError { + /** + * + * @type {Array} + * @memberof ValidationError + */ + loc: Array; + /** + * + * @type {string} + * @memberof ValidationError + */ + msg: string; + /** + * + * @type {string} + * @memberof ValidationError + */ + type: string; +} + +/** + * Check if a given object implements the ValidationError interface. + */ +export function instanceOfValidationError(value: object): value is ValidationError { + if (!('loc' in value) || value['loc'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + if (!('type' in value) || value['type'] === undefined) return false; + return true; +} + +export function ValidationErrorFromJSON(json: any): ValidationError { + return ValidationErrorFromJSONTyped(json, false); +} + +export function ValidationErrorFromJSONTyped(json: any, ignoreDiscriminator: boolean): ValidationError { + if (json == null) { + return json; + } + return { + + 'loc': ((json['loc'] as Array).map(LocationInnerFromJSON)), + 'msg': json['msg'], + 'type': json['type'], + }; +} + +export function ValidationErrorToJSON(value?: ValidationError | null): any { + if (value == null) { + return value; + } + return { + + 'loc': ((value['loc'] as Array).map(LocationInnerToJSON)), + 'msg': value['msg'], + 'type': value['type'], + }; +} + diff --git a/src/api/pfdcm/generated/models/XinetdCore.ts b/src/api/pfdcm/generated/models/XinetdCore.ts new file mode 100644 index 000000000..bb66f4c72 --- /dev/null +++ b/src/api/pfdcm/generated/models/XinetdCore.ts @@ -0,0 +1,124 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The core data model + * @export + * @interface XinetdCore + */ +export interface XinetdCore { + /** + * + * @type {string} + * @memberof XinetdCore + */ + servicePort: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + tmpDir: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + logDir: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + dataDir: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + listener: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + patientMapDir: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + studyMapDir: string; + /** + * + * @type {string} + * @memberof XinetdCore + */ + seriesMapDir: string; +} + +/** + * Check if a given object implements the XinetdCore interface. + */ +export function instanceOfXinetdCore(value: object): value is XinetdCore { + if (!('servicePort' in value) || value['servicePort'] === undefined) return false; + if (!('tmpDir' in value) || value['tmpDir'] === undefined) return false; + if (!('logDir' in value) || value['logDir'] === undefined) return false; + if (!('dataDir' in value) || value['dataDir'] === undefined) return false; + if (!('listener' in value) || value['listener'] === undefined) return false; + if (!('patientMapDir' in value) || value['patientMapDir'] === undefined) return false; + if (!('studyMapDir' in value) || value['studyMapDir'] === undefined) return false; + if (!('seriesMapDir' in value) || value['seriesMapDir'] === undefined) return false; + return true; +} + +export function XinetdCoreFromJSON(json: any): XinetdCore { + return XinetdCoreFromJSONTyped(json, false); +} + +export function XinetdCoreFromJSONTyped(json: any, ignoreDiscriminator: boolean): XinetdCore { + if (json == null) { + return json; + } + return { + + 'servicePort': json['servicePort'], + 'tmpDir': json['tmpDir'], + 'logDir': json['logDir'], + 'dataDir': json['dataDir'], + 'listener': json['listener'], + 'patientMapDir': json['patient_mapDir'], + 'studyMapDir': json['study_mapDir'], + 'seriesMapDir': json['series_mapDir'], + }; +} + +export function XinetdCoreToJSON(value?: XinetdCore | null): any { + if (value == null) { + return value; + } + return { + + 'servicePort': value['servicePort'], + 'tmpDir': value['tmpDir'], + 'logDir': value['logDir'], + 'dataDir': value['dataDir'], + 'listener': value['listener'], + 'patient_mapDir': value['patientMapDir'], + 'study_mapDir': value['studyMapDir'], + 'series_mapDir': value['seriesMapDir'], + }; +} + diff --git a/src/api/pfdcm/generated/models/XinetdDBPutModel.ts b/src/api/pfdcm/generated/models/XinetdDBPutModel.ts new file mode 100644 index 000000000..42df069aa --- /dev/null +++ b/src/api/pfdcm/generated/models/XinetdDBPutModel.ts @@ -0,0 +1,68 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { XinetdCore } from './XinetdCore'; +import { + XinetdCoreFromJSON, + XinetdCoreFromJSONTyped, + XinetdCoreToJSON, +} from './XinetdCore'; + +/** + * + * @export + * @interface XinetdDBPutModel + */ +export interface XinetdDBPutModel { + /** + * + * @type {XinetdCore} + * @memberof XinetdDBPutModel + */ + info: XinetdCore; +} + +/** + * Check if a given object implements the XinetdDBPutModel interface. + */ +export function instanceOfXinetdDBPutModel(value: object): value is XinetdDBPutModel { + if (!('info' in value) || value['info'] === undefined) return false; + return true; +} + +export function XinetdDBPutModelFromJSON(json: any): XinetdDBPutModel { + return XinetdDBPutModelFromJSONTyped(json, false); +} + +export function XinetdDBPutModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): XinetdDBPutModel { + if (json == null) { + return json; + } + return { + + 'info': XinetdCoreFromJSON(json['info']), + }; +} + +export function XinetdDBPutModelToJSON(value?: XinetdDBPutModel | null): any { + if (value == null) { + return value; + } + return { + + 'info': XinetdCoreToJSON(value['info']), + }; +} + diff --git a/src/api/pfdcm/generated/models/XinetdDBReturnModel.ts b/src/api/pfdcm/generated/models/XinetdDBReturnModel.ts new file mode 100644 index 000000000..5a1e54384 --- /dev/null +++ b/src/api/pfdcm/generated/models/XinetdDBReturnModel.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelsListenerModelTime } from './ModelsListenerModelTime'; +import { + ModelsListenerModelTimeFromJSON, + ModelsListenerModelTimeFromJSONTyped, + ModelsListenerModelTimeToJSON, +} from './ModelsListenerModelTime'; +import type { XinetdCore } from './XinetdCore'; +import { + XinetdCoreFromJSON, + XinetdCoreFromJSONTyped, + XinetdCoreToJSON, +} from './XinetdCore'; + +/** + * + * @export + * @interface XinetdDBReturnModel + */ +export interface XinetdDBReturnModel { + /** + * + * @type {XinetdCore} + * @memberof XinetdDBReturnModel + */ + info: XinetdCore; + /** + * + * @type {ModelsListenerModelTime} + * @memberof XinetdDBReturnModel + */ + timeCreated: ModelsListenerModelTime; + /** + * + * @type {ModelsListenerModelTime} + * @memberof XinetdDBReturnModel + */ + timeModified: ModelsListenerModelTime; + /** + * + * @type {string} + * @memberof XinetdDBReturnModel + */ + message: string; +} + +/** + * Check if a given object implements the XinetdDBReturnModel interface. + */ +export function instanceOfXinetdDBReturnModel(value: object): value is XinetdDBReturnModel { + if (!('info' in value) || value['info'] === undefined) return false; + if (!('timeCreated' in value) || value['timeCreated'] === undefined) return false; + if (!('timeModified' in value) || value['timeModified'] === undefined) return false; + if (!('message' in value) || value['message'] === undefined) return false; + return true; +} + +export function XinetdDBReturnModelFromJSON(json: any): XinetdDBReturnModel { + return XinetdDBReturnModelFromJSONTyped(json, false); +} + +export function XinetdDBReturnModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): XinetdDBReturnModel { + if (json == null) { + return json; + } + return { + + 'info': XinetdCoreFromJSON(json['info']), + 'timeCreated': ModelsListenerModelTimeFromJSON(json['time_created']), + 'timeModified': ModelsListenerModelTimeFromJSON(json['time_modified']), + 'message': json['message'], + }; +} + +export function XinetdDBReturnModelToJSON(value?: XinetdDBReturnModel | null): any { + if (value == null) { + return value; + } + return { + + 'info': XinetdCoreToJSON(value['info']), + 'time_created': ModelsListenerModelTimeToJSON(value['timeCreated']), + 'time_modified': ModelsListenerModelTimeToJSON(value['timeModified']), + 'message': value['message'], + }; +} + diff --git a/src/api/pfdcm/generated/models/index.ts b/src/api/pfdcm/generated/models/index.ts new file mode 100644 index 000000000..e1fb462d3 --- /dev/null +++ b/src/api/pfdcm/generated/models/index.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './AboutModel'; +export * from './BodyPACSPypxApiV1PACSSyncPypxPost'; +export * from './BodyPACSServiceHandlerApiV1PACSThreadPypxPost'; +export * from './BodyPACSobjPortUpdateApiV1PACSservicePortPost'; +export * from './DcmtkCore'; +export * from './DcmtkDBPutModel'; +export * from './DcmtkDBReturnModel'; +export * from './Dicom'; +export * from './EchoModel'; +export * from './HTTPValidationError'; +export * from './HelloModel'; +export * from './ListenerDBreturnModel'; +export * from './ListenerHandlerStatus'; +export * from './LocationInner'; +export * from './ModelsListenerModelTime'; +export * from './ModelsListenerModelValueStr'; +export * from './ModelsPacsQRmodelValueStr'; +export * from './ModelsPacsSetupModelTime'; +export * from './ModelsPacsSetupModelValueStr'; +export * from './ModelsSmdbSetupModelValueStr'; +export * from './PACSasync'; +export * from './PACSdbPutModel'; +export * from './PACSdbReturnModel'; +export * from './PACSqueryCore'; +export * from './PACSsetupCore'; +export * from './SMDBFsConfig'; +export * from './SMDBFsCore'; +export * from './SMDBFsReturnModel'; +export * from './SMDBcubeConfig'; +export * from './SMDBcubeCore'; +export * from './SMDBcubeReturnModel'; +export * from './SMDBswiftConfig'; +export * from './SMDBswiftCore'; +export * from './SysInfoModel'; +export * from './ValidationError'; +export * from './XinetdCore'; +export * from './XinetdDBPutModel'; +export * from './XinetdDBReturnModel'; diff --git a/src/api/pfdcm/generated/runtime.ts b/src/api/pfdcm/generated/runtime.ts new file mode 100644 index 000000000..1e066c3d4 --- /dev/null +++ b/src/api/pfdcm/generated/runtime.ts @@ -0,0 +1,426 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * pfdcm + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 3.1.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/src/api/pfdcm/index.test.ts b/src/api/pfdcm/index.test.ts new file mode 100644 index 000000000..aad4940cc --- /dev/null +++ b/src/api/pfdcm/index.test.ts @@ -0,0 +1,28 @@ +import { test, expect } from "vitest"; +import { PfdcmClient, Configuration } from "./index.ts"; +import { PfdcmEnvironmentalDetailApi } from "./generated"; + +test("PfdcmClient", async (context) => { + const url = process.env.VITE_PFDCM_URL; + // const url = 'http://localhost:8088' + const configuration = new Configuration({ basePath: url }); + try { + const pfdcmDetailClient = new PfdcmEnvironmentalDetailApi(configuration); + const hello = await pfdcmDetailClient.readHelloApiV1HelloGet(); + expect(hello.name).toBe("pfdcm_hello"); + } catch (e) { + // pfdcm 'hello' endpoint not working, is pfdcm online? + context.skip(); + } + + const client = new PfdcmClient(configuration); + expect(await client.getPacsServices()).toContain("MINICHRISORTHANC"); + + const list = await client.query("MINICHRISORTHANC", { patientID: "1449c1d" }); + const { study, series } = list[0]; + expect(study.PatientName).toBe("anonymized"); + expect(study.PatientBirthDate).toStrictEqual(new Date(2009, 6, 1)); + expect(series.map((s) => s.SeriesDescription.trim())).toContain( + "SAG MPRAGE 220 FOV", + ); +}); diff --git a/src/api/pfdcm/index.ts b/src/api/pfdcm/index.ts new file mode 100644 index 000000000..9f75cddcd --- /dev/null +++ b/src/api/pfdcm/index.ts @@ -0,0 +1,3 @@ +export { Configuration } from "./generated"; +export { PfdcmClient } from "./client"; +export type { PACSqueryCore } from "./generated"; diff --git a/src/api/pfdcm/models.ts b/src/api/pfdcm/models.ts new file mode 100644 index 000000000..3b1818f9d --- /dev/null +++ b/src/api/pfdcm/models.ts @@ -0,0 +1,88 @@ +import { PACSqueryCore } from "./generated"; + +type PypxTag = { + tag: 0 | string; + value: 0 | string; + label: string; +}; + +type Pypx = { + status: "success" | "error"; + command: string; + data: ReadonlyArray<{ + [key: string]: PypxTag | ReadonlyArray<{ [key: string]: PypxTag }>; + }>; + args: PACSqueryCore; +}; + +/** + * PFDCM "find" endpoint response, in its unprocessed and ugly original form. + * + * See https://github.com/FNNDSC/pfdcm/blob/3.1.22/pfdcm/controllers/pacsQRcontroller.py#L171-L176 + */ +type PypxFind = { + status: boolean; + find: {}; + message: string; + PACSdirective: PACSqueryCore; + pypx: Pypx; +}; + +/** + * DICOM study metadata. + */ +type Study = { + SpecificCharacterSet: string; + StudyDate: Date | null; + AccessionNumber: string; + RetrieveAETitle: string; + ModalitiesInStudy: string; + StudyDescription: string; + PatientName: string; + PatientID: string; + PatientBirthDate: Date | null; + PatientSex: string; + PatientAge: string; + ProtocolName: string; + AcquisitionProtocolName: string; + AcquisitionProtocolDescription: string; + StudyInstanceUID: string; + NumberOfStudyRelatedSeries: string; + PerformedStationAETitle: string; +}; + +/** + * DICOM series metadata. + */ +type Series = { + SpecificCharacterSet: string; + StudyDate: string; + SeriesDate: string; + AccessionNumber: string; + RetrieveAETitle: string; + Modality: string; + StudyDescription: string; + SeriesDescription: string; + PatientName: string; + PatientID: string; + PatientBirthDate: Date | null; + PatientSex: string; + PatientAge: string; + ProtocolName: string; + AcquisitionProtocolName: string; + AcquisitionProtocolDescription: string; + StudyInstanceUID: string; + SeriesInstanceUID: string; + NumberOfSeriesRelatedInstances: number | null; + PerformedStationAETitle: string; +}; + +/** + * PACS query response data. + */ +type StudyAndSeries = { + study: Study; + series: ReadonlyArray; +}; + +export type { PypxFind, Pypx, PypxTag, Study, Series, StudyAndSeries }; diff --git a/src/api/testHelpers.ts b/src/api/testHelpers.ts new file mode 100644 index 000000000..cdc3c37e6 --- /dev/null +++ b/src/api/testHelpers.ts @@ -0,0 +1,34 @@ +import { vi } from "vitest"; +import ChrisClient, { DownloadToken } from "@fnndsc/chrisapi"; +import WS from "vitest-websocket-mock"; + +/** + * Helper function for mocking LONK-WS. + */ +function createMockCubePacsWs( + port: number, + id: number = 55, +): [ChrisClient, WS] { + const fakeChrisHost = `localhost:${port}`; + const fakeChrisUrl = `http://${fakeChrisHost}/api/v1/`; + const fakeChrisAuth = { token: "12345" }; + vi.spyOn(DownloadToken.prototype, "data", "get").mockReturnValue({ + token: "abcdefgnotarealjwt", + }); + const fakeDownloadToken = new DownloadToken( + `${fakeChrisUrl}downloadtokens/${id}/`, + fakeChrisAuth, + ); + + const client = new ChrisClient(fakeChrisUrl, fakeChrisAuth); + client.createDownloadToken = vi.fn(async () => fakeDownloadToken); + + const ws = new WS( + `ws://${fakeChrisHost}/api/v1/pacs/ws/?token=abcdefgnotarealjwt`, + { jsonProtocol: true }, + ); + + return [client, ws]; +} + +export { createMockCubePacsWs }; diff --git a/src/app.css b/src/app.css index f8dfc6e6a..7bbdcc8ac 100644 --- a/src/app.css +++ b/src/app.css @@ -85,3 +85,10 @@ body { justify-content: center !important; align-items: center !important; } + +.patternfly-font { + font-family: var(--pf-v5-global--FontFamily--text); + font-size: var(--pf-v5-global--FontSize--md); + line-height: var(--pf-v5-global--LineHeight--md); + font-weight: var(--pf-v5-global--FontWeight--normal); +} diff --git a/src/components/NiivueDatasetViewer/NiivueDatasetViewer.tsx b/src/components/NiivueDatasetViewer/NiivueDatasetViewer.tsx index 9fba2fbb2..483b65fc2 100644 --- a/src/components/NiivueDatasetViewer/NiivueDatasetViewer.tsx +++ b/src/components/NiivueDatasetViewer/NiivueDatasetViewer.tsx @@ -17,7 +17,7 @@ import { getDataset, getPreClient, } from "./client"; -import { flexRowSpaceBetween, hideOnMobile } from "./cssUtils"; +import { flexRowSpaceBetween, hideOnMobile } from "../../cssUtils.ts"; import { FpClient } from "../../api/fp/chrisapi"; import { pipe } from "fp-ts/function"; import * as TE from "fp-ts/TaskEither"; diff --git a/src/components/NiivueDatasetViewer/components/SettingsTab.tsx b/src/components/NiivueDatasetViewer/components/SettingsTab.tsx index eea783d71..65c529909 100644 --- a/src/components/NiivueDatasetViewer/components/SettingsTab.tsx +++ b/src/components/NiivueDatasetViewer/components/SettingsTab.tsx @@ -11,7 +11,7 @@ import { Switch, } from "@patternfly/react-core"; import RadiologcalConventionToggle from "./RadiologcalConventionToggle"; -import { hideOnDesktop } from "../cssUtils"; +import { hideOnDesktop } from "../../../cssUtils"; import Spacing from "@patternfly/react-styles/css/utilities/Spacing/spacing"; import { css } from "@patternfly/react-styles"; import SliceTypeButton from "./SliceTypeButton"; diff --git a/src/components/NiivueDatasetViewer/content/footer.tsx b/src/components/NiivueDatasetViewer/content/footer.tsx index 3cfa11d5d..355fc295d 100644 --- a/src/components/NiivueDatasetViewer/content/footer.tsx +++ b/src/components/NiivueDatasetViewer/content/footer.tsx @@ -1,4 +1,4 @@ -import { hideOnDesktop, hideOnMobileInline } from "../cssUtils.ts"; +import { hideOnDesktop, hideOnMobileInline } from "../../../cssUtils.ts"; import { Chip, Popover, TextContent } from "@patternfly/react-core"; import BUILD_VERSION from "../../../getBuildVersion.ts"; import { css } from "@patternfly/react-styles"; diff --git a/src/components/Pacs/PacsController.test.tsx b/src/components/Pacs/PacsController.test.tsx new file mode 100644 index 000000000..5b9cc465e --- /dev/null +++ b/src/components/Pacs/PacsController.test.tsx @@ -0,0 +1,53 @@ +import { screen } from "@testing-library/react"; +import { expect, test, vi } from "vitest"; +import PacsQRApp from "./PacsController.tsx"; +import { Configuration as PfdcmConfig, PfdcmClient } from "../../api/pfdcm"; +import { renderWithProviders } from "../../store/testHelpers.tsx"; +import { createMockCubePacsWs } from "../../api/testHelpers.ts"; + +test("PACS Q/R page can bootstrap", async () => { + const pfdcmClient = createPfdcmMock(async () => ["BCH", "MGH", "BWH"]); + const [chrisClient, ws] = createMockCubePacsWs(32584); + + const getClientMocks = { + getChrisClient: vi.fn(() => chrisClient), + getPfdcmClient: vi.fn(() => pfdcmClient), + }; + renderWithProviders(); + + await ws.connected; + + await expect + .poll(() => screen.getByPlaceholderText("Search for DICOM studies by MRN")) + .toBeInTheDocument(); + + // First PACS service (besides 'default') should be automatically selected. + expect(screen.getByTitle("PACS service")).toHaveTextContent("BCH"); +}); + +test("Shows error screen if PFDCM is offline", async () => { + const pfdcmClient = createPfdcmMock(async () => { + throw new Error("I am an expected error"); + }); + const [chrisClient, _ws] = createMockCubePacsWs(32583); + + const getClientMocks = { + getChrisClient: vi.fn(() => chrisClient), + getPfdcmClient: vi.fn(() => pfdcmClient), + }; + renderWithProviders(); + + await expect + .poll(() => screen.getByText("I am an expected error")) + .toBeInTheDocument(); + expect( + screen.getByText(/PACS Q\/R application is currently unavailable/), + ).toBeInTheDocument(); +}); + +function createPfdcmMock(getPacsServices: PfdcmClient["getPacsServices"]) { + const pfdcmConfig = new PfdcmConfig({ basePath: "https://example.com" }); + const pfdcmClient = new PfdcmClient(pfdcmConfig); + pfdcmClient.getPacsServices = vi.fn(getPacsServices); + return pfdcmClient; +} diff --git a/src/components/Pacs/PacsController.tsx b/src/components/Pacs/PacsController.tsx new file mode 100644 index 000000000..f408eb4a9 --- /dev/null +++ b/src/components/Pacs/PacsController.tsx @@ -0,0 +1,641 @@ +/** + * The primary PACS Q/R UI code is found in ./PacsView.tsx. This file defines + * a component which wraps the default export from ./PacsView.tsx, which + * manages effects and state. + */ + +import React from "react"; +import { PACSqueryCore, PfdcmClient } from "../../api/pfdcm"; +import Client, { PACSSeries } from "@fnndsc/chrisapi"; +import { App } from "antd"; +import { PageSection } from "@patternfly/react-core"; +import PacsView from "./PacsView.tsx"; +import PacsLoadingScreen from "./components/PacsLoadingScreen.tsx"; +import ErrorScreen from "./components/ErrorScreen.tsx"; +import { + skipToken, + useMutation, + useQueries, + useQuery, +} from "@tanstack/react-query"; +import mergeStates, { SeriesQueryZip } from "./mergeStates.ts"; +import { + DEFAULT_RECEIVE_STATE, + IPacsState, + PacsPullRequestState, + ReceiveState, + RequestState, + SeriesPullState, + SeriesReceiveState, + StudyKey, +} from "./types.ts"; +import { DEFAULT_PREFERENCES } from "./defaultPreferences.ts"; +import { toStudyKey, zipPacsNameAndSeriesUids } from "./helpers.ts"; +import { useImmer } from "use-immer"; +import SeriesMap from "../../api/lonk/seriesMap.ts"; +import { useLonk } from "../../api/lonk"; +import { produce, WritableDraft } from "immer"; +import { + isFromPacs, + sameSeriesInstanceUidAs, + sameStudyInstanceUidAs, +} from "./curry.ts"; +import { Study } from "../../api/pfdcm/models.ts"; + +type PacsControllerProps = { + getPfdcmClient: () => PfdcmClient; + getChrisClient: () => Client; +}; + +/** + * ChRIS_ui "PACS Query and Retrieve" controller + view. + * + * ## Purpose + * + * This component handles all the state and effects for {@link PacsView}, + * which includes: + * + * - Managing the CUBE and PFDCM client objects and making requests with them + * - Connecting to the progress notifications WebSocket at `api/v1/pacs/ws/` + * - Knowing the state of which DICOM series are received, receiving, or + * ready to receive. + * - Dispatching requests for querying and retrieving DICOM from PACS via PFDCM. + * + * ## Bootstrapping + * + * The PACS Q/R application needs to be "bootstrapped" which means: + * + * 1. Making an initial connection to PFDCM + * 2. Connecting to the PACS receive progress WebSocket, `api/v1/pacs/ws/` + * + * During bootstrapping, a loading screen is shown. If bootstrapping fails, + * an error screen is shown. + * + * ## PACS Retrieve Workflow + * + * ChRIS_ui and the rest of CUBE work together to implement the following + * behavior: + * + * 1. User queries for data, which is resolved by PFDCM. + * 2. User clicks to expand a DICOM study, showing a list of series. + * 3. For each series... + * 4. ChRIS_ui subscribes to a series' notifications via LONK + * 5. ChRIS_ui checks CUBE whether a series exists in CUBE + * 6. When both subscription and existence check is complete, + * and the series does not exist in CUBE, ChRIS_ui is ready + * to pull the DICOM series. + * 7. During the reception of a DICOM series, `status.done === false` + * 8. After the reception of a DICOM series, ChRIS enters a "waiting" + * state while the task to register the DICOM series is enqueued + * or running. + * 9. The DICOM series will appear in CUBE after being registered. + */ +const PacsController: React.FC = ({ + getChrisClient, + getPfdcmClient, +}) => { + // ======================================== + // CLIENTS AND MISC + // ======================================== + + const { message } = App.useApp(); + + const pfdcmClient = React.useMemo(getPfdcmClient, [getPfdcmClient]); + const chrisClient = React.useMemo(getChrisClient, [getChrisClient]); + + // ======================================== + // STATE + // ======================================== + + /** + * Indicates a fatal error with the WebSocket. + */ + const [wsError, setWsError] = React.useState(null); + // TODO create a settings component for changing preferences + const [preferences, setPreferences] = React.useState(DEFAULT_PREFERENCES); + + /** + * The state of DICOM series, according to LONK. + */ + const [receiveState, setReceiveState] = useImmer( + new SeriesMap(), + ); + + /** + * List of PACS queries which the user wants to pull. + */ + const [pullRequests, setPullRequests] = useImmer< + ReadonlyArray + >([]); + + /** + * Update the state of a pull request. + */ + const updatePullRequestState = React.useCallback( + ( + service: string, + query: PACSqueryCore, + delta: Partial>, + ) => + setPullRequests((draft) => { + const i = draft.findLastIndex( + (pr) => + pr.service === service && + (pr.query.studyInstanceUID + ? pr.query.studyInstanceUID === query.studyInstanceUID + : true) && + (pr.query.seriesInstanceUID + ? pr.query.seriesInstanceUID === query.seriesInstanceUID + : true), + ); + if (i === -1) { + throw new Error( + "pullFromPacs mutation called on unknown pull request: " + + `service=${service}, query=${JSON.stringify(query)}`, + ); + } + draft[i] = { ...draft[i], ...delta }; + }), + [setPullRequests], + ); + + // ======================================== + // PFDCM QUERIES AND DATA + // ======================================== + + const [{ service, query }, setPacsQuery] = React.useState<{ + service?: string; + query?: PACSqueryCore; + }>({}); + + /** + * List of PACS servers which PFDCM can talk to. + */ + const pfdcmServices = useQuery({ + queryKey: ["pfdcmServices"], + queryFn: () => pfdcmClient.getPacsServices(), + }); + + /** + * The state of DICOM studies and series in PFDCM. + */ + const pfdcmStudies = useQuery({ + queryKey: React.useMemo( + () => ["pfdcmStudies", service, query], + [service, query], + ), + queryFn: + service && query ? () => pfdcmClient.query(service, query) : skipToken, + }); + + // ======================================== + // EXPANDED STUDIES AND SERIES STATE + // ======================================== + + /** + * Studies which have their series visible on-screen. + */ + const [expandedStudies, setExpandedStudies] = useImmer< + ReadonlyArray + >([]); + + /** + * The StudyInstanceUIDs of all expanded studies. + */ + const expandedStudyUids = React.useMemo( + () => expandedStudies.map((s) => s.StudyInstanceUID), + [expandedStudies], + ); + + /** + * List of series which are currently visible on-screen. + */ + const expandedSeries = React.useMemo( + () => zipPacsNameAndSeriesUids(expandedStudies, pfdcmStudies.data), + [expandedStudies, pfdcmStudies.data], + ); + + const changeExpandedStudies = React.useCallback( + (pacs_name: string, StudyInstanceUIDs: ReadonlyArray) => { + setExpandedStudies( + StudyInstanceUIDs.map((StudyInstanceUID) => ({ + StudyInstanceUID, + pacs_name, + })), + ); + }, + [setExpandedStudies], + ); + + const appendExpandedStudies = React.useCallback( + (studies: Pick[]) => + setExpandedStudies((draft) => { + draft.push(...studies.map(toStudyKey)); + }), + [setExpandedStudies], + ); + + /** + * Expand the studies of the query. + */ + const expandStudiesFor = React.useCallback( + (pacs_name: string, query: PACSqueryCore) => { + if (!pfdcmStudies.data) { + throw new Error( + "Expanding studies is not currently possible because we do not " + + "have data from PFDCM yet.", + ); + } + if (query.seriesInstanceUID) { + const studies = pfdcmStudies.data + .filter(isFromPacs(pacs_name)) + .flatMap((study) => study.series) + .filter(sameSeriesInstanceUidAs(query)); + appendExpandedStudies(studies); + return; + } + if (!query.seriesInstanceUID && query.studyInstanceUID) { + const studies = pfdcmStudies.data + .filter(isFromPacs(pacs_name)) + .map((s) => s.study) + .filter(sameStudyInstanceUidAs(query)); + appendExpandedStudies(studies); + return; + } + }, + [pfdcmStudies.data, appendExpandedStudies], + ); + + // ======================================== + // CUBE QUERIES AND DATA + // ======================================== + + /** + * Check whether CUBE has any of the series that are expanded. + */ + const cubeSeriesQuery = useQueries({ + queries: expandedSeries.map((series) => ({ + queryKey: ["cubeSeries", chrisClient.url, series], + queryFn: async () => { + const list = await chrisClient.getPACSSeriesList({ + limit: 1, + ...series, + }); + const items: PACSSeries[] | null = list.getItems(); + // https://github.com/FNNDSC/fnndsc/issues/101 + if (items === null) { + return null; + } + return items[0] || null; + }, + })), + }); + + /** + * Zip together elements of `cubeSeriesQuery` and the parameters used for + * each element. + */ + const cubeSeriesQueryZip: ReadonlyArray = React.useMemo( + () => + expandedSeries.map((search, i) => ({ + search, + result: cubeSeriesQuery[i], + })), + [expandedSeries, cubeSeriesQuery], + ); + // + // /** + // * Poll CUBE for the existence of DICOM series which have been reported as + // * "done" by LONK. It is necessary to poll CUBE because there will be a delay + // * between when LONK reports the series as "done" and when CUBE will run the + // * celery task of finally registering the series. + // */ + // const finalCheckNeedingSeries = useQueries({ + // queries: React.useMemo( + // () => + // receiveState.entries().map(([pacs_name, SeriesInstanceUID, state]) => ({ + // queryKey: [ + // "finalCheckNeedingSeries", + // pacs_name, + // SeriesInstanceUID, + // state, + // ], + // queryFn: async () => { + // const search = { pacs_name, SeriesInstanceUID, limit: 1 }; + // const list = await chrisClient.getPACSSeriesList(search); + // const items = list.getItems() as ReadonlyArray; + // if (items.length === 0) { + // throw Error("not found"); + // } + // return items[0]; + // }, + // enabled: state.done, // TODO CANCEL POLLING ONCE WE FOUND THE FILE. + // })), + // [receiveState], + // ), + // }); + + /** + * Combined states of PFDCM, LONK, and CUBE into one object. + */ + const studies = React.useMemo(() => { + if (!pfdcmStudies.data) { + return null; + } + return mergeStates( + pfdcmStudies.data, + pullRequests, + cubeSeriesQueryZip, + receiveState, + ); + }, [mergeStates, pfdcmStudies, cubeSeriesQueryZip, receiveState]); + + /** + * Entire state of the Pacs Q/R application. + */ + const state: IPacsState = React.useMemo(() => { + return { preferences, studies }; + }, [preferences, studies]); + + const error = React.useMemo( + () => wsError || pfdcmServices.error?.message, + [wsError, pfdcmServices.error], + ); + + // ======================================== + // LONK WEBSOCKET + // ======================================== + + const getSeriesDescriptionOr = React.useCallback( + (pacs_name: string, SeriesInstanceUID: string) => { + if (!pfdcmStudies.data) { + return SeriesInstanceUID; + } + const series = pfdcmStudies.data + .flatMap((s) => s.series) + .find( + (s) => + s.SeriesInstanceUID === SeriesInstanceUID && + s.RetrieveAETitle === pacs_name, + ); + if (!series) { + return SeriesInstanceUID; + } + return series.SeriesDescription; + }, + [pfdcmStudies.data], + ); + + /** + * Update (or insert) the state of a series' reception. + */ + const updateReceiveState = React.useCallback( + ( + pacs_name: string, + SeriesInstanceUID: string, + recipe: (draft: WritableDraft) => void, + ) => + setReceiveState((draft) => { + const prevState = + draft.get(pacs_name, SeriesInstanceUID) || DEFAULT_RECEIVE_STATE; + const nextState = produce(prevState, recipe); + draft.set(pacs_name, SeriesInstanceUID, nextState); + }), + [setReceiveState], + ); + + const lonk = useLonk({ + client: chrisClient, + onDone(pacs_name: string, SeriesInstanceUID: string) { + updateReceiveState(pacs_name, SeriesInstanceUID, (draft) => { + draft.done = true; + }); + }, + onProgress(pacs_name: string, SeriesInstanceUID: string, ndicom: number) { + updateReceiveState(pacs_name, SeriesInstanceUID, (draft) => { + draft.receivedCount = ndicom; + }); + }, + onError(pacs_name: string, SeriesInstanceUID: string, error: string) { + updateReceiveState(pacs_name, SeriesInstanceUID, (draft) => { + draft.errors.push(error); + }); + const desc = getSeriesDescriptionOr(pacs_name, SeriesInstanceUID); + message.error( + <>There was an error while receiving the series "{desc}", + ); + }, + onMessageError(data: any, error: string) { + console.error("LONK message error", error, data); + message.error( + <> + A LONK error occurred, please check the console. + , + ); + }, + retryOnError: true, + reconnectAttempts: 3, + reconnectInterval: 3000, + shouldReconnect(e) { + return e.code < 400 || e.code > 499; + }, + onReconnectStop() { + setWsError(<>The WebSocket is disconnected.); + }, + onWebsocketError() { + message.error( + <>There was an error with the WebSocket. Reconnecting…, + ); + }, + }); + + // ======================================== + // CALLBACKS + // ======================================== + + /** + * Fetch studies from PFDCM. + */ + const onSubmit = React.useCallback( + (service: string, query: PACSqueryCore) => { + setPacsQuery({ service, query }); + }, + [setPacsQuery], + ); + + // ======================================== + // PACS RETRIEVAL + // ======================================== + + const onRetrieve = React.useCallback( + (service: string, query: PACSqueryCore) => { + expandStudiesFor(service, query); + setPullRequests((draft) => { + // indicate that the user requests for something to be retrieved. + draft.push({ + state: RequestState.NOT_REQUESTED, + query, + service, + }); + }); + }, + [setPullRequests, expandStudiesFor], + ); + + /** + * @returns true if the study does not contain any series which are `NOT_CHECKED` or `CHECKING`. + */ + const shouldPullStudy = React.useCallback( + (pacs_name: string, StudyInstanceUID: string) => + (studies ?? []) + .filter( + ({ info }) => + info.StudyInstanceUID === StudyInstanceUID && + info.RetrieveAETitle === pacs_name, + ) + .flatMap((study) => study.series) + .findIndex( + ({ pullState }) => + pullState === SeriesPullState.NOT_CHECKED || + pullState == SeriesPullState.CHECKING, + ) === -1, + [studies], + ); + + /** + * All series states. + */ + const allSeries = React.useMemo( + () => (studies ?? []).flatMap((s) => s.series), + [studies], + ); + + /** + * @returns true if the series is ready to pull. + */ + const shouldPullSeries = React.useCallback( + (pacs_name: string, SeriesInstanceUID: string) => + allSeries.findIndex( + ({ info, pullState }) => + info.RetrieveAETitle === pacs_name && + info.SeriesInstanceUID === SeriesInstanceUID && + pullState === SeriesPullState.READY, + ) !== -1, + [allSeries], + ); + + /** + * Whether we should send the pull request (where the pull request + * may be for either a DICOM study or series). + */ + const shouldSendPullRequest = React.useCallback( + (pullRequest: PacsPullRequestState): boolean => { + if (pullRequest.state !== RequestState.NOT_REQUESTED) { + return false; + } + if ( + pullRequest.query.studyInstanceUID && + !("seriesInstanceUID" in pullRequest.query) + ) { + return shouldPullStudy( + pullRequest.service, + pullRequest.query.studyInstanceUID, + ); + } + if (pullRequest.query.seriesInstanceUID) { + return shouldPullSeries( + pullRequest.service, + pullRequest.query.seriesInstanceUID, + ); + } + return false; + }, + [shouldPullStudy, shouldPullSeries], + ); + + /** + * Send request to PFDCM to pull from PACS. + */ + const pullFromPacs = useMutation({ + mutationFn: ({ service, query }: PacsPullRequestState) => + pfdcmClient.retrieve(service, query), + onMutate: ({ service, query }: PacsPullRequestState) => + updatePullRequestState(service, query, { + state: RequestState.REQUESTING, + }), + onError: (error, { service, query }) => + updatePullRequestState(service, query, { error: error }), + onSuccess: (_, { service, query }) => + updatePullRequestState(service, query, { state: RequestState.REQUESTED }), + onSettled: (data, error, variables, context) => { + console.dir({ + event: "settled", + data, + error, + variables, + context, + }); + }, + }); + + React.useEffect(() => { + pullRequests + .filter(shouldSendPullRequest) + .forEach((pr) => pullFromPacs.mutate(pr)); + }, [pullRequests, shouldSendPullRequest]); + + // ======================================== + // EFFECTS + // ======================================== + + // Set document title + React.useEffect(() => { + const originalTitle = document.title; + document.title = "ChRIS PACS"; + return () => { + document.title = originalTitle; + }; + }, []); + + // Subscribe to all expanded series + React.useEffect(() => { + for (const { pacs_name, SeriesInstanceUID } of expandedSeries) { + lonk + .subscribe(pacs_name, SeriesInstanceUID) + .then(({ pacs_name, SeriesInstanceUID }) => { + updateReceiveState(pacs_name, SeriesInstanceUID, (draft) => { + draft.subscribed = true; + }); + }); + } + // Note: we are subscribing to series, but never unsubscribing. + // This is mostly harmless. + }, [expandedSeries]); + + // ======================================== + // RENDER + // ======================================== + + return ( + + {error ? ( + {error} + ) : pfdcmServices.data ? ( + + ) : ( + + )} + + ); +}; + +export type { PacsControllerProps }; +export default PacsController; diff --git a/src/components/Pacs/PacsView.tsx b/src/components/Pacs/PacsView.tsx new file mode 100644 index 000000000..046f8f1c1 --- /dev/null +++ b/src/components/Pacs/PacsView.tsx @@ -0,0 +1,92 @@ +import React from "react"; +import PacsInput, { PacsInputProps } from "./components/PacsInput.tsx"; +import PacsStudiesView, { + PacsStudiesViewProps, +} from "./components/PacsStudiesView.tsx"; +import { getDefaultPacsService } from "./components/helpers.ts"; +import { useSearchParams } from "react-router-dom"; +import { PACSqueryCore } from "../../api/pfdcm"; +import { Empty, Flex, Spin } from "antd"; +import { IPacsState } from "./types.ts"; + +type PacsViewProps = Pick & + Pick & { + onRetrieve: (service: string, query: PACSqueryCore) => void; + onStudyExpand: ( + service: string, + StudyInstanceUIDs: ReadonlyArray, + ) => void; + state: IPacsState; + isLoadingStudies?: boolean; + }; + +/** + * PACS Query and Retrieve view component. + * + * This component has a text input field for specifying a PACS query, + * and will display studies and series found in PACS. + */ +const PacsView: React.FC = ({ + state: { preferences, studies }, + services, + onSubmit, + onRetrieve, + expandedStudyUids, + onStudyExpand, + isLoadingStudies, +}) => { + const [searchParams, setSearchParams] = useSearchParams(); + const defaultService = React.useMemo( + () => getDefaultPacsService(services), + [services], + ); + const service = searchParams.get("service") || defaultService; + const setService = (service: string) => + setSearchParams((searchParams) => { + searchParams.set("service", service); + return searchParams; + }); + + const curriedOnRetrieve = React.useCallback( + (query: PACSqueryCore) => onRetrieve(service, query), + [onRetrieve], + ); + + const curriedOnStudyExpand = React.useCallback( + (StudyInstanceUIDS: ReadonlyArray) => + onStudyExpand(service, StudyInstanceUIDS), + [onStudyExpand], + ); + + return ( + <> + +
+ {studies ? ( + + + + ) : ( + + + + )} + + ); +}; + +export default PacsView; diff --git a/src/components/Pacs/components/ErrorScreen.tsx b/src/components/Pacs/components/ErrorScreen.tsx new file mode 100644 index 000000000..c69d06171 --- /dev/null +++ b/src/components/Pacs/components/ErrorScreen.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { + EmptyState, + EmptyStateBody, + EmptyStateHeader, + EmptyStateIcon, + Text, + TextContent, + TextVariants, +} from "@patternfly/react-core"; +import { ExclamationCircleIcon } from "../../Icons"; + +const ErrorScreen: React.FC> = ({ children }) => ( + + } + /> + + + {children} + + The ChRIS PACS Q/R application is currently unavailable. + Please contact your ChRIS admin by clicking{" "} + here. + + + + +); + +export default ErrorScreen; diff --git a/src/components/Pacs/components/ModalityBadges.tsx b/src/components/Pacs/components/ModalityBadges.tsx new file mode 100644 index 000000000..5a53a3dd5 --- /dev/null +++ b/src/components/Pacs/components/ModalityBadges.tsx @@ -0,0 +1,22 @@ +import { Flex, Tag } from "antd"; + +const ModalityBadges: React.FC<{ modalities: string }> = ({ modalities }) => { + const modalityList = modalities + .split(/\s+|\\/) + .map((s) => s.trim()) + .reduce( + (acc: string[], cur) => (acc.includes(cur) ? acc : acc.concat(cur)), + [], + ); + return ( + + {modalityList.map((modality) => ( + + {modality} + + ))} + + ); +}; + +export default ModalityBadges; diff --git a/src/components/Pacs/components/PacsInput.tsx b/src/components/Pacs/components/PacsInput.tsx new file mode 100644 index 000000000..dec5be300 --- /dev/null +++ b/src/components/Pacs/components/PacsInput.tsx @@ -0,0 +1,162 @@ +import React from "react"; +import { PACSqueryCore } from "../../../api/pfdcm"; +import { Select, Input, Row, Col, Grid, Segmented } from "antd"; +import { useSearchParams } from "react-router-dom"; +import { useBooleanSearchParam } from "./helpers.ts"; +import { ReadonlyNonEmptyArray } from "fp-ts/ReadonlyNonEmptyArray"; + +type InputFieldProps = { + onSubmit: (query: PACSqueryCore) => void; +}; + +type PacsInputProps = { + services: ReadonlyNonEmptyArray; + service: string; + setService: (service: string) => void; + onSubmit: (service: string, query: PACSqueryCore) => void; +}; + +/** + * An input field for searching in PACS by MRN + */ +const MrnInput: React.FC = ({ onSubmit }) => { + const [searchParams, setSearchParams] = useSearchParams(); + + const onClear = () => { + setSearchParams((searchParams) => { + searchParams.delete("mrn"); + return searchParams; + }); + }; + + const submitMrn = (value?: string) => { + if (!value) { + return; + } + setSearchParams((searchParams) => { + searchParams.set("mrn", value.trim()); + onSubmit({ patientID: value.trim() }); + return searchParams; + }); + }; + + // on first page load: if URI contains ?mrn=... then submit + // the search right away + React.useEffect(() => { + const initialValue = searchParams.get("mrn"); + if (initialValue && initialValue.trim().length > 0) { + onSubmit({ patientID: initialValue.trim() }); + } + }, [searchParams, onSubmit]); + + return ( + submitMrn(e.currentTarget.value)} + onSearch={submitMrn} + enterButton={true} + /> + ); +}; + +/** + * An advanced search input field for searching in PACS by PatientID, AccessionNumber, ... + */ +const AdvancedInput: React.FC = ({ onSubmit }) => { + return ( + <> +

Advanced search not implemented.

+ + ); +}; + +/** + * A `` which shows different text on mobile vs desktop layouts. + */ +const ScreenSizeSpan: React.FC<{ + mobile: React.ReactNode; + desktop: React.ReactNode; +}> = ({ mobile, desktop }) => { + const screens = Grid.useBreakpoint(); + return screens.md ? desktop : mobile; +}; + +const PacsInput: React.FC = ({ + onSubmit, + service, + setService, + services, +}) => { + const [isAdvancedSearch, setIsAdvancedSearch] = useBooleanSearchParam( + useSearchParams(), + "advancedSearch", + ); + + const curriedOnSubmit = React.useMemo( + () => (query: PACSqueryCore) => onSubmit(service, query), + [service, onSubmit], + ); + const input = React.useMemo( + () => + isAdvancedSearch ? ( + + ) : ( + + ), + [isAdvancedSearch, curriedOnSubmit], + ); + const advancedSearchToggle = ( + , + value: "mrnOnly", + }, + { + label: , + value: "advanced", + }, + ]} + value={isAdvancedSearch ? "advanced" : "mrnOnly"} + onChange={(v) => setIsAdvancedSearch(v === "advanced")} + /> + ); + + const serviceDropdown = ( +
+