diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b405b47cb6be68df9ed8e7d7941244f2cc22f643..cab254a8cead5777e77d2180acf280bf8cdfdbab 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: node:16 +image: node:18 cache: paths: - node_modules diff --git a/Dockerfile b/Dockerfile index a4eba23cfd2e33d53d8ed21c5b0c529bb034a520..831ee92da98a9c44ebee34cd9920c5db395bbf85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # build stage -FROM node:16 as build-stage +FROM node:18 as build-stage WORKDIR /app HEALTHCHECK --interval=35s --timeout=4s CMD curl -f http://localhost || exit 1 # RUN apk add yarn @@ -11,7 +11,7 @@ RUN npm run build-only # production stage FROM nginx:stable-alpine as production-stage -HEALTHCHECK --interval=35s --timeout=4s CMD curl --head -f http://localhost || exit 1 +HEALTHCHECK --interval=305s --timeout=4s CMD curl --head -f http://localhost || exit 1 COPY --from=build-stage /app/dist /usr/share/nginx/html COPY --from=build-stage /app/src/assets/env.template.js /tmp COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/index.html b/index.html index 482eda96a495b02a952cbced079a488220542251..a39f962f8a08306b6fe930a84d29aa9f1b8d0cab 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang="en"> +<html lang="en" data-bs-theme="light"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> diff --git a/package-lock.json b/package-lock.json index 75e4957e6d01c0f0c5b50f7a248a554cf5126249..b3a54f3c78f2e2fb405ee781cd260f03b86e8d53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,46 +11,47 @@ "@aws-sdk/client-s3": "^3.379.1", "@aws-sdk/lib-storage": "^3.379.1", "@aws-sdk/s3-request-presigner": "^3.379.1", - "@fortawesome/fontawesome-free": "^6.4.0", - "@popperjs/core": "^2.11.8", - "ajv": "^8.12.0", - "bootstrap": "^5.2.3", - "chart.js": "^4.3.2", - "chartjs-plugin-zoom": "^2.0.1", - "dayjs": "^1.11.9", - "dompurify": "^3.0.5", - "filesize": "^10.0.8", - "pinia": "^2.1.6", - "semver": "^7.5.4", - "showdown": "^2.1.0", - "vue": "^3.3.4", - "vue-router": "^4.2.4", - "vue3-cookies": "^1.0.6" + "@fortawesome/fontawesome-free": "~6.4.2", + "@popperjs/core": "~2.11.8", + "ajv": "~8.12.0", + "bootstrap": "~5.3.1", + "chart.js": "~4.3.3", + "chartjs-plugin-zoom": "~2.0.1", + "dayjs": "~1.11.9", + "dompurify": "~3.0.5", + "filesize": "~10.0.12", + "pinia": "~2.1.6", + "semver": "~7.5.4", + "showdown": "~2.1.0", + "vue": "~3.3.4", + "vue-router": "~4.2.4", + "vue3-cookies": "~1.0.6" }, "devDependencies": { - "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@esbuild-plugins/node-modules-polyfill": "^0.2.2", - "@rushstack/eslint-patch": "^1.2.0", - "@types/bootstrap": "^5.2.6", - "@types/dompurify": "^3.0.2", - "@types/node": "^16.11.45", - "@types/semver": "^7.5.0", - "@types/showdown": "^2.0.1", - "@vitejs/plugin-vue": "^4.2.3", - "@vue/eslint-config-prettier": "^7.1.0", - "@vue/eslint-config-typescript": "^11.0.2", - "@vue/tsconfig": "^0.1.3", - "axios": "^1.4.0", - "eslint": "^8.46.0", - "eslint-plugin-vue": "^9.16.1", - "npm-run-all": "^4.1.5", + "@esbuild-plugins/node-globals-polyfill": "~0.2.3", + "@esbuild-plugins/node-modules-polyfill": "~0.2.2", + "@rushstack/eslint-patch": "~1.2.0", + "@tsconfig/node18": "^18.2.1", + "@types/bootstrap": "~5.2.6", + "@types/dompurify": "~3.0.2", + "@types/node": "^16.18.48", + "@types/semver": "~7.5.1", + "@types/showdown": "~2.0.1", + "@vitejs/plugin-vue": "~4.3.4", + "@vue/eslint-config-prettier": "~8.0.0", + "@vue/eslint-config-typescript": "~11.0.3", + "@vue/tsconfig": "~0.4.0", + "axios": "~1.5.0", + "eslint": "~8.48.0", + "eslint-plugin-vue": "~9.17.0", + "npm-run-all": "~4.1.5", "openapi-typescript-codegen": "^0.25.0", - "prettier": "^2.8.4", - "rollup-plugin-node-polyfills": "^0.2.1", - "sass": "^1.59.3", - "typescript": "~4.9.5", - "vite": "^4.2.3", - "vue-tsc": "^1.8.8" + "prettier": "~3.0.3", + "rollup-plugin-node-polyfills": "~0.2.1", + "sass": "~1.66.1", + "typescript": "~5.1.6", + "vite": "~4.4.9", + "vue-tsc": "~1.8.10" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1272,9 +1273,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -1317,18 +1318,18 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", - "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", + "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==", "hasInstallScript": true, "engines": { "node": ">=6" @@ -1418,6 +1419,32 @@ "node": ">= 8" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pkgr/utils/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -2024,6 +2051,12 @@ "node": ">=14.0.0" } }, + "node_modules/@tsconfig/node18": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.1.tgz", + "integrity": "sha512-RDDZFuofwkcKpl8Vpj5wFbY+H53xOtqK7ckEL1sXsbPwvKwDdjQf3LkHbtt9sxIHn9nWIEwkmCwBRZ6z5TKU2A==", + "dev": true + }, "node_modules/@types/bootstrap": { "version": "5.2.6", "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz", @@ -2049,15 +2082,15 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz", - "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==", + "version": "16.18.48", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.48.tgz", + "integrity": "sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==", "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, "node_modules/@types/showdown": { @@ -2073,17 +2106,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz", - "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/type-utils": "5.56.0", - "@typescript-eslint/utils": "5.56.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -2107,14 +2140,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz", - "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { @@ -2134,13 +2167,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz", - "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2151,13 +2184,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz", - "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.56.0", - "@typescript-eslint/utils": "5.56.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -2178,9 +2211,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", - "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2191,13 +2224,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", - "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2218,17 +2251,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz", - "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -2244,12 +2277,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz", - "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2261,9 +2294,9 @@ } }, "node_modules/@vitejs/plugin-vue": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz", - "integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz", + "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==", "dev": true, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -2274,30 +2307,30 @@ } }, "node_modules/@volar/language-core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.0.tgz", - "integrity": "sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz", + "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==", "dev": true, "dependencies": { - "@volar/source-map": "1.10.0" + "@volar/source-map": "1.10.1" } }, "node_modules/@volar/source-map": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.0.tgz", - "integrity": "sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz", + "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==", "dev": true, "dependencies": { "muggle-string": "^0.3.1" } }, "node_modules/@volar/typescript": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.0.tgz", - "integrity": "sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz", + "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==", "dev": true, "dependencies": { - "@volar/language-core": "1.10.0" + "@volar/language-core": "1.10.1" } }, "node_modules/@vue/compiler-core": { @@ -2373,28 +2406,28 @@ "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" }, "node_modules/@vue/eslint-config-prettier": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-7.1.0.tgz", - "integrity": "sha512-Pv/lVr0bAzSIHLd9iz0KnvAr4GKyCEl+h52bc4e5yWuDVtLgFwycF7nrbWTAQAS+FU6q1geVd07lc6EWfJiWKQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", + "integrity": "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==", "dev": true, "dependencies": { - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^4.0.0" + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0" }, "peerDependencies": { - "eslint": ">= 7.28.0", - "prettier": ">= 2.0.0" + "eslint": ">= 8.0.0", + "prettier": ">= 3.0.0" } }, "node_modules/@vue/eslint-config-typescript": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.2.tgz", - "integrity": "sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.3.tgz", + "integrity": "sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "vue-eslint-parser": "^9.0.0" + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "vue-eslint-parser": "^9.1.1" }, "engines": { "node": "^14.17.0 || >=16.0.0" @@ -2411,9 +2444,9 @@ } }, "node_modules/@vue/language-core": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.8.tgz", - "integrity": "sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.10.tgz", + "integrity": "sha512-db8PtM4ZZr7SYNH30XpKxUYnUBYaTvcuJ4c2whKK04fuAjbtjAIZ2al5GzGEfUlesmvkpgdbiSviRXUxgD9Omw==", "dev": true, "dependencies": { "@volar/language-core": "~1.10.0", @@ -2531,27 +2564,19 @@ "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==" }, "node_modules/@vue/tsconfig": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.1.3.tgz", - "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==", - "dev": true, - "peerDependencies": { - "@types/node": "*" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.4.0.tgz", + "integrity": "sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==", + "dev": true }, "node_modules/@vue/typescript": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.8.tgz", - "integrity": "sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.10.tgz", + "integrity": "sha512-vPSpTXMk4chYwvyTGjM891cKgnx2r6vtbdANOp2mRU31f4HYGyLrZBlGgiua7SaO2cLjUg8y91OipJe0t8OFhA==", "dev": true, "dependencies": { "@volar/typescript": "~1.10.0", - "@vue/language-core": "1.8.8" + "@vue/language-core": "1.8.10" } }, "node_modules/acorn": { @@ -2674,9 +2699,9 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -2709,6 +2734,15 @@ } ] }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2725,9 +2759,9 @@ "dev": true }, "node_modules/bootstrap": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", - "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz", + "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==", "funding": [ { "type": "github", @@ -2739,7 +2773,7 @@ } ], "peerDependencies": { - "@popperjs/core": "^2.11.6" + "@popperjs/core": "^2.11.8" } }, "node_modules/bowser": { @@ -2747,6 +2781,18 @@ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2778,6 +2824,21 @@ "ieee754": "^1.1.4" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -2835,9 +2896,9 @@ } }, "node_modules/chart.js": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz", - "integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", + "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -3004,6 +3065,52 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -3196,15 +3303,15 @@ } }, "node_modules/eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3215,7 +3322,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -3262,30 +3369,38 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } } }, "node_modules/eslint-plugin-vue": { - "version": "9.16.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.16.1.tgz", - "integrity": "sha512-2FtnTqazA6aYONfDuOZTk0QzwhAwi7Z4+uJ7+GHeGxcKapjqWlDsRWDenvyG/utyOfAS5bVRmAG3cEWiYEz2bA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz", + "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -3317,9 +3432,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3466,21 +3581,44 @@ "node": ">=0.8.x" } }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3560,9 +3698,9 @@ } }, "node_modules/filesize": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.8.tgz", - "integrity": "sha512-/ylBrxZ1GAjgh2CEemKKLwTtmXfWqTtN1jRl6iqLwnMEucUX5cmaCCUPGstQOHVCcK9uYL6o1cPNakLQU2sasQ==", + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.12.tgz", + "integrity": "sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==", "engines": { "node": ">= 10.4.0" } @@ -3738,6 +3876,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -3787,9 +3937,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3854,12 +4004,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3991,6 +4135,15 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4178,6 +4331,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4199,6 +4367,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -4272,6 +4458,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -4333,6 +4531,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4477,6 +4702,12 @@ "node": ">= 0.10.0" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4520,6 +4751,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4607,9 +4850,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -4734,9 +4977,9 @@ } }, "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -4787,6 +5030,33 @@ "which": "bin/which" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -4844,6 +5114,39 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/openapi-typescript-codegen": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.25.0.tgz", @@ -5121,15 +5424,15 @@ } }, "node_modules/prettier": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.6.tgz", - "integrity": "sha512-mtuzdiBbHwPEgl7NxWlqOkithPyp4VN93V7VeHVWBF+ad3I5avc0RVDT4oImXQy9H/AqxA2NSQH8pSxHW6FYbQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -5309,9 +5612,9 @@ } }, "node_modules/rollup": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.0.tgz", - "integrity": "sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==", + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.0.tgz", + "integrity": "sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -5354,6 +5657,110 @@ "estree-walker": "^0.6.1" } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5411,9 +5818,9 @@ } }, "node_modules/sass": { - "version": "1.59.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.59.3.tgz", - "integrity": "sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -5424,7 +5831,7 @@ "sass": "sass.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/semver": { @@ -5500,6 +5907,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5665,6 +6078,18 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5706,12 +6131,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5789,16 +6242,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uglify-js": { @@ -5838,6 +6291,15 @@ "node": ">= 10.0.0" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5870,14 +6332,14 @@ } }, "node_modules/vite": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz", - "integrity": "sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", "dev": true, "dependencies": { "esbuild": "^0.18.10", - "postcss": "^8.4.26", - "rollup": "^3.25.2" + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" @@ -6007,13 +6469,13 @@ } }, "node_modules/vue-tsc": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.8.tgz", - "integrity": "sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.10.tgz", + "integrity": "sha512-ptpTFFDoHQgkWJF7i5iERxooiQzOGtG1uKTfmAUuS3qPuSQGq+Ky/S8BFHhnFGwoOxq/PjmGN2QSZEfg1rtzQA==", "dev": true, "dependencies": { - "@vue/language-core": "1.8.8", - "@vue/typescript": "1.8.8", + "@vue/language-core": "1.8.10", + "@vue/typescript": "1.8.10", "semver": "^7.3.8" }, "bin": { @@ -7018,9 +7480,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -7055,15 +7517,15 @@ } }, "@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@fortawesome/fontawesome-free": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", - "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==" + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", + "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==" }, "@humanwhocodes/config-array": { "version": "0.11.10", @@ -7130,6 +7592,28 @@ "fastq": "^1.6.0" } }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + } + } + }, "@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -7615,6 +8099,12 @@ "tslib": "^2.5.0" } }, + "@tsconfig/node18": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.1.tgz", + "integrity": "sha512-RDDZFuofwkcKpl8Vpj5wFbY+H53xOtqK7ckEL1sXsbPwvKwDdjQf3LkHbtt9sxIHn9nWIEwkmCwBRZ6z5TKU2A==", + "dev": true + }, "@types/bootstrap": { "version": "5.2.6", "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz", @@ -7640,15 +8130,15 @@ "dev": true }, "@types/node": { - "version": "16.18.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz", - "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==", + "version": "16.18.48", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.48.tgz", + "integrity": "sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==", "dev": true }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, "@types/showdown": { @@ -7664,17 +8154,17 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz", - "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/type-utils": "5.56.0", - "@typescript-eslint/utils": "5.56.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -7682,53 +8172,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz", - "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz", - "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" } }, "@typescript-eslint/type-utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz", - "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.56.0", - "@typescript-eslint/utils": "5.56.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", - "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", - "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -7737,63 +8227,63 @@ } }, "@typescript-eslint/utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz", - "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz", - "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, "@vitejs/plugin-vue": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz", - "integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz", + "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==", "dev": true, "requires": {} }, "@volar/language-core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.0.tgz", - "integrity": "sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz", + "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==", "dev": true, "requires": { - "@volar/source-map": "1.10.0" + "@volar/source-map": "1.10.1" } }, "@volar/source-map": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.0.tgz", - "integrity": "sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz", + "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==", "dev": true, "requires": { "muggle-string": "^0.3.1" } }, "@volar/typescript": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.0.tgz", - "integrity": "sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz", + "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==", "dev": true, "requires": { - "@volar/language-core": "1.10.0" + "@volar/language-core": "1.10.1" } }, "@vue/compiler-core": { @@ -7870,30 +8360,30 @@ "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" }, "@vue/eslint-config-prettier": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-7.1.0.tgz", - "integrity": "sha512-Pv/lVr0bAzSIHLd9iz0KnvAr4GKyCEl+h52bc4e5yWuDVtLgFwycF7nrbWTAQAS+FU6q1geVd07lc6EWfJiWKQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", + "integrity": "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==", "dev": true, "requires": { - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^4.0.0" + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0" } }, "@vue/eslint-config-typescript": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.2.tgz", - "integrity": "sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.3.tgz", + "integrity": "sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==", "dev": true, "requires": { - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "vue-eslint-parser": "^9.0.0" + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "vue-eslint-parser": "^9.1.1" } }, "@vue/language-core": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.8.tgz", - "integrity": "sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.10.tgz", + "integrity": "sha512-db8PtM4ZZr7SYNH30XpKxUYnUBYaTvcuJ4c2whKK04fuAjbtjAIZ2al5GzGEfUlesmvkpgdbiSviRXUxgD9Omw==", "dev": true, "requires": { "@volar/language-core": "~1.10.0", @@ -7995,20 +8485,19 @@ "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==" }, "@vue/tsconfig": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.1.3.tgz", - "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==", - "dev": true, - "requires": {} + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.4.0.tgz", + "integrity": "sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==", + "dev": true }, "@vue/typescript": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.8.tgz", - "integrity": "sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.10.tgz", + "integrity": "sha512-vPSpTXMk4chYwvyTGjM891cKgnx2r6vtbdANOp2mRU31f4HYGyLrZBlGgiua7SaO2cLjUg8y91OipJe0t8OFhA==", "dev": true, "requires": { "@volar/typescript": "~1.10.0", - "@vue/language-core": "1.8.8" + "@vue/language-core": "1.8.10" } }, "acorn": { @@ -8095,9 +8584,9 @@ "dev": true }, "axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "dev": true, "requires": { "follow-redirects": "^1.15.0", @@ -8116,6 +8605,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -8129,9 +8624,9 @@ "dev": true }, "bootstrap": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", - "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz", + "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==", "requires": {} }, "bowser": { @@ -8139,6 +8634,15 @@ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8167,6 +8671,15 @@ "ieee754": "^1.1.4" } }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -8206,9 +8719,9 @@ } }, "chart.js": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz", - "integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", + "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", "requires": { "@kurkle/color": "^0.3.0" } @@ -8331,6 +8844,34 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, "define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -8480,15 +9021,15 @@ "dev": true }, "eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -8499,7 +9040,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -8568,18 +9109,19 @@ "requires": {} }, "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" } }, "eslint-plugin-vue": { - "version": "9.16.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.16.1.tgz", - "integrity": "sha512-2FtnTqazA6aYONfDuOZTk0QzwhAwi7Z4+uJ7+GHeGxcKapjqWlDsRWDenvyG/utyOfAS5bVRmAG3cEWiYEz2bA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz", + "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", @@ -8602,9 +9144,9 @@ } }, "eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { @@ -8675,21 +9217,38 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, + "execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -8749,9 +9308,9 @@ } }, "filesize": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.8.tgz", - "integrity": "sha512-/ylBrxZ1GAjgh2CEemKKLwTtmXfWqTtN1jRl6iqLwnMEucUX5cmaCCUPGstQOHVCcK9uYL6o1cPNakLQU2sasQ==" + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.12.tgz", + "integrity": "sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==" }, "fill-range": { "version": "7.0.1", @@ -8873,6 +9432,12 @@ "has-symbols": "^1.0.3" } }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -8907,9 +9472,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8953,12 +9518,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -9046,6 +9605,12 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -9174,6 +9739,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -9189,6 +9760,15 @@ "is-extglob": "^2.1.1" } }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -9235,6 +9815,12 @@ "call-bind": "^1.0.2" } }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -9275,6 +9861,23 @@ "call-bind": "^1.0.2" } }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -9392,6 +9995,12 @@ "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9423,6 +10032,12 @@ "mime-db": "1.52.0" } }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9492,9 +10107,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -9589,9 +10204,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -9629,6 +10244,23 @@ } } }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -9671,6 +10303,27 @@ "wrappy": "1" } }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, "openapi-typescript-codegen": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.25.0.tgz", @@ -9840,9 +10493,9 @@ "dev": true }, "prettier": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.6.tgz", - "integrity": "sha512-mtuzdiBbHwPEgl7NxWlqOkithPyp4VN93V7VeHVWBF+ad3I5avc0RVDT4oImXQy9H/AqxA2NSQH8pSxHW6FYbQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "prettier-linter-helpers": { @@ -9961,9 +10614,9 @@ } }, "rollup": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.0.tgz", - "integrity": "sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==", + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.0.tgz", + "integrity": "sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -9998,6 +10651,76 @@ "estree-walker": "^0.6.1" } }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10024,9 +10747,9 @@ } }, "sass": { - "version": "1.59.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.59.3.tgz", - "integrity": "sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -10082,6 +10805,12 @@ "object-inspect": "^1.9.0" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -10213,6 +10942,12 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -10239,12 +10974,28 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -10303,9 +11054,9 @@ } }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "devOptional": true }, "uglify-js": { @@ -10333,6 +11084,12 @@ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10362,15 +11119,15 @@ } }, "vite": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz", - "integrity": "sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", "dev": true, "requires": { "esbuild": "^0.18.10", "fsevents": "~2.3.2", - "postcss": "^8.4.26", - "rollup": "^3.25.2" + "postcss": "^8.4.27", + "rollup": "^3.27.1" } }, "vue": { @@ -10437,13 +11194,13 @@ } }, "vue-tsc": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.8.tgz", - "integrity": "sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.10.tgz", + "integrity": "sha512-ptpTFFDoHQgkWJF7i5iERxooiQzOGtG1uKTfmAUuS3qPuSQGq+Ky/S8BFHhnFGwoOxq/PjmGN2QSZEfg1rtzQA==", "dev": true, "requires": { - "@vue/language-core": "1.8.8", - "@vue/typescript": "1.8.8", + "@vue/language-core": "1.8.10", + "@vue/typescript": "1.8.10", "semver": "^7.3.8" } }, diff --git a/package.json b/package.json index 3666525993ddd2774d8d32fbb31084271cf6c776..4f1c1caaa7f9b2bc16f6fdee3ee370ae9dcd2c92 100644 --- a/package.json +++ b/package.json @@ -8,53 +8,54 @@ "build-only": "vite build", "type-check": "vue-tsc --noEmit", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", - "generate-s3-client": "openapi --input https://clowm-staging.bi.denbi.de/api/s3proxy-service/openapi.json --output src/client/s3proxy --client axios", - "generate-auth-client": "openapi --input https://clowm-staging.bi.denbi.de/api/auth-service/openapi.json --output src/client/auth --client axios", - "generate-workflow-client": "openapi --input https://clowm-staging.bi.denbi.de/api/workflow-service/openapi.json --output src/client/workflow --client axios" + "generate-s3-client": "openapi --input http://localhost:9999/api/s3proxy-service/openapi.json --output src/client/s3proxy --client axios", + "generate-auth-client": "openapi --input http://localhost:9999/api/auth-service/openapi.json --output src/client/auth --client axios", + "generate-workflow-client": "openapi --input http://localhost:9999/api/workflow-service/openapi.json --output src/client/workflow --client axios" }, "dependencies": { "@aws-sdk/client-s3": "^3.379.1", "@aws-sdk/lib-storage": "^3.379.1", "@aws-sdk/s3-request-presigner": "^3.379.1", - "@fortawesome/fontawesome-free": "^6.4.0", - "@popperjs/core": "^2.11.8", - "ajv": "^8.12.0", - "bootstrap": "^5.2.3", - "chart.js": "^4.3.2", - "chartjs-plugin-zoom": "^2.0.1", - "dayjs": "^1.11.9", - "dompurify": "^3.0.5", - "filesize": "^10.0.8", - "pinia": "^2.1.6", - "semver": "^7.5.4", - "showdown": "^2.1.0", - "vue": "^3.3.4", - "vue-router": "^4.2.4", - "vue3-cookies": "^1.0.6" + "@fortawesome/fontawesome-free": "~6.4.2", + "@popperjs/core": "~2.11.8", + "ajv": "~8.12.0", + "bootstrap": "~5.3.1", + "chart.js": "~4.3.3", + "chartjs-plugin-zoom": "~2.0.1", + "dayjs": "~1.11.9", + "dompurify": "~3.0.5", + "filesize": "~10.0.12", + "pinia": "~2.1.6", + "semver": "~7.5.4", + "showdown": "~2.1.0", + "vue": "~3.3.4", + "vue-router": "~4.2.4", + "vue3-cookies": "~1.0.6" }, "devDependencies": { - "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@esbuild-plugins/node-modules-polyfill": "^0.2.2", - "@rushstack/eslint-patch": "^1.2.0", - "@types/bootstrap": "^5.2.6", - "@types/dompurify": "^3.0.2", - "@types/node": "^16.11.45", - "@types/semver": "^7.5.0", - "@types/showdown": "^2.0.1", - "@vitejs/plugin-vue": "^4.2.3", - "@vue/eslint-config-prettier": "^7.1.0", - "@vue/eslint-config-typescript": "^11.0.2", - "@vue/tsconfig": "^0.1.3", - "axios": "^1.4.0", - "eslint": "^8.46.0", - "eslint-plugin-vue": "^9.16.1", - "npm-run-all": "^4.1.5", + "@esbuild-plugins/node-globals-polyfill": "~0.2.3", + "@esbuild-plugins/node-modules-polyfill": "~0.2.2", + "@rushstack/eslint-patch": "~1.2.0", + "@tsconfig/node18": "^18.2.1", + "@types/bootstrap": "~5.2.6", + "@types/dompurify": "~3.0.2", + "@types/node": "^16.18.48", + "@types/semver": "~7.5.1", + "@types/showdown": "~2.0.1", + "@vitejs/plugin-vue": "~4.3.4", + "@vue/eslint-config-prettier": "~8.0.0", + "@vue/eslint-config-typescript": "~11.0.3", + "@vue/tsconfig": "~0.4.0", + "axios": "~1.5.0", + "eslint": "~8.48.0", + "eslint-plugin-vue": "~9.17.0", + "npm-run-all": "~4.1.5", "openapi-typescript-codegen": "^0.25.0", - "prettier": "^2.8.4", - "rollup-plugin-node-polyfills": "^0.2.1", - "sass": "^1.59.3", - "typescript": "~4.9.5", - "vite": "^4.2.3", - "vue-tsc": "^1.8.8" + "prettier": "~3.0.3", + "rollup-plugin-node-polyfills": "~0.2.1", + "sass": "~1.66.1", + "typescript": "~5.1.6", + "vite": "~4.4.9", + "vue-tsc": "~1.8.10" } } diff --git a/src/assets/base.css b/src/assets/base.css index 6b40a5ee14a7249f6935bc5967bedf02728976d5..c797992b2ac10ceba1399cfc918fc09cf7276cf5 100644 --- a/src/assets/base.css +++ b/src/assets/base.css @@ -47,8 +47,6 @@ body { min-height: 100vh; - color: var(--color-text); - background: var(--color-background); transition: color 0.5s, background-color 0.5s; line-height: 1.6; font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, diff --git a/src/client/auth/core/ApiError.ts b/src/client/auth/core/ApiError.ts index 99d792996765118614adf6a232bc8766b95710e9..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648 100644 --- a/src/client/auth/core/ApiError.ts +++ b/src/client/auth/core/ApiError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/core/ApiRequestOptions.ts b/src/client/auth/core/ApiRequestOptions.ts index c7b77538c5ce6aa48f141541392121fbcafc4821..c19adcc94dc5f015865368d6d64751de5c243aa3 100644 --- a/src/client/auth/core/ApiRequestOptions.ts +++ b/src/client/auth/core/ApiRequestOptions.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/core/ApiResult.ts b/src/client/auth/core/ApiResult.ts index b095dc7708af2aa405a6dc7ad08f423334bc7f24..ad8fef2bc334c2aedf5c043896fd394229453ff0 100644 --- a/src/client/auth/core/ApiResult.ts +++ b/src/client/auth/core/ApiResult.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/core/CancelablePromise.ts b/src/client/auth/core/CancelablePromise.ts index 26ad303915af161f170cc144a5e7684be3053778..55fef8517238d2dab7478598eefca72e657b9fb5 100644 --- a/src/client/auth/core/CancelablePromise.ts +++ b/src/client/auth/core/CancelablePromise.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -22,15 +23,13 @@ export interface OnCancel { } export class CancelablePromise<T> implements Promise<T> { - readonly [Symbol.toStringTag]!: string; - - private _isResolved: boolean; - private _isRejected: boolean; - private _isCancelled: boolean; - private readonly _cancelHandlers: (() => void)[]; - private readonly _promise: Promise<T>; - private _resolve?: (value: T | PromiseLike<T>) => void; - private _reject?: (reason?: any) => void; + #isResolved: boolean; + #isRejected: boolean; + #isCancelled: boolean; + readonly #cancelHandlers: (() => void)[]; + readonly #promise: Promise<T>; + #resolve?: (value: T | PromiseLike<T>) => void; + #reject?: (reason?: any) => void; constructor( executor: ( @@ -39,78 +38,82 @@ export class CancelablePromise<T> implements Promise<T> { onCancel: OnCancel ) => void ) { - this._isResolved = false; - this._isRejected = false; - this._isCancelled = false; - this._cancelHandlers = []; - this._promise = new Promise<T>((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; + this.#isResolved = false; + this.#isRejected = false; + this.#isCancelled = false; + this.#cancelHandlers = []; + this.#promise = new Promise<T>((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; const onResolve = (value: T | PromiseLike<T>): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isResolved = true; - this._resolve?.(value); + this.#isResolved = true; + this.#resolve?.(value); }; const onReject = (reason?: any): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isRejected = true; - this._reject?.(reason); + this.#isRejected = true; + this.#reject?.(reason); }; const onCancel = (cancelHandler: () => void): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._cancelHandlers.push(cancelHandler); + this.#cancelHandlers.push(cancelHandler); }; Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this._isResolved, + get: (): boolean => this.#isResolved, }); Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this._isRejected, + get: (): boolean => this.#isRejected, }); Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this._isCancelled, + get: (): boolean => this.#isCancelled, }); return executor(onResolve, onReject, onCancel as OnCancel); }); } + get [Symbol.toStringTag]() { + return "Cancellable Promise"; + } + public then<TResult1 = T, TResult2 = never>( onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null ): Promise<TResult1 | TResult2> { - return this._promise.then(onFulfilled, onRejected); + return this.#promise.then(onFulfilled, onRejected); } public catch<TResult = never>( onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null ): Promise<T | TResult> { - return this._promise.catch(onRejected); + return this.#promise.catch(onRejected); } public finally(onFinally?: (() => void) | null): Promise<T> { - return this._promise.finally(onFinally); + return this.#promise.finally(onFinally); } public cancel(): void { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isCancelled = true; - if (this._cancelHandlers.length) { + this.#isCancelled = true; + if (this.#cancelHandlers.length) { try { - for (const cancelHandler of this._cancelHandlers) { + for (const cancelHandler of this.#cancelHandlers) { cancelHandler(); } } catch (error) { @@ -118,11 +121,11 @@ export class CancelablePromise<T> implements Promise<T> { return; } } - this._cancelHandlers.length = 0; - this._reject?.(new CancelError('Request aborted')); + this.#cancelHandlers.length = 0; + this.#reject?.(new CancelError('Request aborted')); } public get isCancelled(): boolean { - return this._isCancelled; + return this.#isCancelled; } } diff --git a/src/client/auth/core/OpenAPI.ts b/src/client/auth/core/OpenAPI.ts index 27edd44a9d6ec44fc05b177eb541f5a518d7d285..9484fb91af3169d50a2a4fee46d01bed59b08907 100644 --- a/src/client/auth/core/OpenAPI.ts +++ b/src/client/auth/core/OpenAPI.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -11,11 +12,11 @@ export type OpenAPIConfig = { VERSION: string; WITH_CREDENTIALS: boolean; CREDENTIALS: 'include' | 'omit' | 'same-origin'; - TOKEN?: string | Resolver<string>; - USERNAME?: string | Resolver<string>; - PASSWORD?: string | Resolver<string>; - HEADERS?: Headers | Resolver<Headers>; - ENCODE_PATH?: (path: string) => string; + TOKEN?: string | Resolver<string> | undefined; + USERNAME?: string | Resolver<string> | undefined; + PASSWORD?: string | Resolver<string> | undefined; + HEADERS?: Headers | Resolver<Headers> | undefined; + ENCODE_PATH?: ((path: string) => string) | undefined; }; export const OpenAPI: OpenAPIConfig = { diff --git a/src/client/auth/core/request.ts b/src/client/auth/core/request.ts index 2b5375ab44ec20863646a5debca820b42c2b7500..1142d43219797c94e09f35ba83a9f5441892ddd2 100644 --- a/src/client/auth/core/request.ts +++ b/src/client/auth/core/request.ts @@ -1,8 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ import axios from 'axios'; -import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; +import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; import FormData from 'form-data'; import { ApiError } from './ApiError'; @@ -12,19 +13,19 @@ import { CancelablePromise } from './CancelablePromise'; import type { OnCancel } from './CancelablePromise'; import type { OpenAPIConfig } from './OpenAPI'; -const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { +export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { return value !== undefined && value !== null; }; -const isString = (value: any): value is string => { +export const isString = (value: any): value is string => { return typeof value === 'string'; }; -const isStringWithValue = (value: any): value is string => { +export const isStringWithValue = (value: any): value is string => { return isString(value) && value !== ''; }; -const isBlob = (value: any): value is Blob => { +export const isBlob = (value: any): value is Blob => { return ( typeof value === 'object' && typeof value.type === 'string' && @@ -37,15 +38,15 @@ const isBlob = (value: any): value is Blob => { ); }; -const isFormData = (value: any): value is FormData => { +export const isFormData = (value: any): value is FormData => { return value instanceof FormData; }; -const isSuccess = (status: number): boolean => { +export const isSuccess = (status: number): boolean => { return status >= 200 && status < 300; }; -const base64 = (str: string): string => { +export const base64 = (str: string): string => { try { return btoa(str); } catch (err) { @@ -54,7 +55,7 @@ const base64 = (str: string): string => { } }; -const getQueryString = (params: Record<string, any>): string => { +export const getQueryString = (params: Record<string, any>): string => { const qs: string[] = []; const append = (key: string, value: any) => { @@ -107,7 +108,7 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return url; }; -const getFormData = (options: ApiRequestOptions): FormData | undefined => { +export const getFormData = (options: ApiRequestOptions): FormData | undefined => { if (options.formData) { const formData = new FormData(); @@ -136,14 +137,14 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => { type Resolver<T> = (options: ApiRequestOptions) => Promise<T>; -const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { +export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { if (typeof resolver === 'function') { return (resolver as Resolver<T>)(options); } return resolver; }; -const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => { +export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => { const token = await resolve(options, config.TOKEN); const username = await resolve(options, config.USERNAME); const password = await resolve(options, config.PASSWORD); @@ -186,21 +187,22 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for return headers; }; -const getRequestBody = (options: ApiRequestOptions): any => { +export const getRequestBody = (options: ApiRequestOptions): any => { if (options.body) { return options.body; } return undefined; }; -const sendRequest = async <T>( +export const sendRequest = async <T>( config: OpenAPIConfig, options: ApiRequestOptions, url: string, body: any, formData: FormData | undefined, headers: Record<string, string>, - onCancel: OnCancel + onCancel: OnCancel, + axiosClient: AxiosInstance ): Promise<AxiosResponse<T>> => { const source = axios.CancelToken.source(); @@ -216,7 +218,7 @@ const sendRequest = async <T>( onCancel(() => source.cancel('The user aborted a request.')); try { - return await axios.request(requestConfig); + return await axiosClient.request(requestConfig); } catch (error) { const axiosError = error as AxiosError<T>; if (axiosError.response) { @@ -226,7 +228,7 @@ const sendRequest = async <T>( } }; -const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => { +export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => { if (responseHeader) { const content = response.headers[responseHeader]; if (isString(content)) { @@ -236,14 +238,14 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string return undefined; }; -const getResponseBody = (response: AxiosResponse<any>): any => { +export const getResponseBody = (response: AxiosResponse<any>): any => { if (response.status !== 204) { return response.data; } return undefined; }; -const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { +export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { const errors: Record<number, string> = { 400: 'Bad Request', 401: 'Unauthorized', @@ -261,7 +263,19 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => } if (!result.ok) { - throw new ApiError(options, result, 'Generic Error'); + const errorStatus = result.status ?? 'unknown'; + const errorStatusText = result.statusText ?? 'unknown'; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return undefined; + } + })(); + + throw new ApiError(options, result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + ); } }; @@ -269,10 +283,11 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => * Request method * @param config The OpenAPI configuration object * @param options The request options from the service + * @param axiosClient The axios client instance to use * @returns CancelablePromise<T> * @throws ApiError */ -export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => { +export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => { return new CancelablePromise(async (resolve, reject, onCancel) => { try { const url = getUrl(config, options); @@ -281,7 +296,7 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options, formData); if (!onCancel.isCancelled) { - const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel); + const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient); const responseBody = getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/client/auth/index.ts b/src/client/auth/index.ts index b4f2ddad0aadc491252a1d254d3125358a304338..69ffa49395db64976f8d79f2dfbedcb90dc7fc5a 100644 --- a/src/client/auth/index.ts +++ b/src/client/auth/index.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/models/ErrorDetail.ts b/src/client/auth/models/ErrorDetail.ts index b01e5aa28734c901620e98f8795e9528004d1513..343c8124f88bce59decea9cba6085126fe143db8 100644 --- a/src/client/auth/models/ErrorDetail.ts +++ b/src/client/auth/models/ErrorDetail.ts @@ -1,9 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ /** + * * Schema for a error due to a rejected request. + * */ export type ErrorDetail = { /** diff --git a/src/client/auth/models/HTTPValidationError.ts b/src/client/auth/models/HTTPValidationError.ts index a8c21354123500873c7548194a36421eb8316c78..c0bcc87cf7f3222638466bc2e5753ca1accf01f7 100644 --- a/src/client/auth/models/HTTPValidationError.ts +++ b/src/client/auth/models/HTTPValidationError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/models/RoleEnum.ts b/src/client/auth/models/RoleEnum.ts index 4b9b63fd334827e077fd1d5155d4f2de6208ef07..c38121aa528efe32ad2db1868fe05ca559006f4b 100644 --- a/src/client/auth/models/RoleEnum.ts +++ b/src/client/auth/models/RoleEnum.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/models/User.ts b/src/client/auth/models/User.ts index d33e8a91ed454283687c3d179c3f38af876679ac..fb73505018f43f56cff2810494177fe5da6c82b6 100644 --- a/src/client/auth/models/User.ts +++ b/src/client/auth/models/User.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -5,7 +6,9 @@ import type { RoleEnum } from './RoleEnum'; /** + * * Schema for a user. + * */ export type User = { /** @@ -19,6 +22,6 @@ export type User = { /** * Roles of the user */ - roles?: Array<RoleEnum>; + roles: (Array<RoleEnum> | null); }; diff --git a/src/client/auth/models/ValidationError.ts b/src/client/auth/models/ValidationError.ts index 16dc86a6807d9ad1f7e9d06638d4bd002682dc42..18997ec72f4103731f38d915508522ba23ba8506 100644 --- a/src/client/auth/models/ValidationError.ts +++ b/src/client/auth/models/ValidationError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/services/AuthService.ts b/src/client/auth/services/AuthService.ts index 4e5156a38076f613a890cbfde32b7cdd14166b54..0472d3962a5f73dac257fe6de7e4c74cfe56f987 100644 --- a/src/client/auth/services/AuthService.ts +++ b/src/client/auth/services/AuthService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/auth/services/UserService.ts b/src/client/auth/services/UserService.ts index 62cdb9335627acda756cf39fc80eecdaaa5de3e1..ae578426600f01927ec075bafe3d68b849366327 100644 --- a/src/client/auth/services/UserService.ts +++ b/src/client/auth/services/UserService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -34,15 +35,15 @@ export class UserService { * Return the users that have a specific substring in their name. * Permission 'user:read_any' required, except when 'name_substring' as only query parameter is set. * Then permission 'user:search' required. - * @param nameSubstring Filter users by a substring in their name. Permission 'search_user' required + * @param nameSubstring Filter users by a substring in their name. Permission 'search' required * @param filterRoles Filter users by their role. If multiple are selected, they are concatenating by an OR Expresssion. Permission 'read_any' required * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission 'read_any' required. * @returns User Successful Response * @throws ApiError */ public static userListUsers( - nameSubstring?: string, - filterRoles?: Array<RoleEnum>, + nameSubstring?: (string | null), + filterRoles?: (Array<RoleEnum> | null), includeRoles: boolean = false, ): CancelablePromise<Array<User>> { return __request(OpenAPI, { @@ -65,6 +66,7 @@ export class UserService { /** * Get a user by its uid * Return the user with the specific uid. A user can only view himself. + * Permission 'user:read' required * @param uid UID of a user * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission 'read_any' required. * @returns User Successful Response diff --git a/src/client/s3proxy/core/ApiError.ts b/src/client/s3proxy/core/ApiError.ts index 99d792996765118614adf6a232bc8766b95710e9..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648 100644 --- a/src/client/s3proxy/core/ApiError.ts +++ b/src/client/s3proxy/core/ApiError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/core/ApiRequestOptions.ts b/src/client/s3proxy/core/ApiRequestOptions.ts index c7b77538c5ce6aa48f141541392121fbcafc4821..c19adcc94dc5f015865368d6d64751de5c243aa3 100644 --- a/src/client/s3proxy/core/ApiRequestOptions.ts +++ b/src/client/s3proxy/core/ApiRequestOptions.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/core/ApiResult.ts b/src/client/s3proxy/core/ApiResult.ts index b095dc7708af2aa405a6dc7ad08f423334bc7f24..ad8fef2bc334c2aedf5c043896fd394229453ff0 100644 --- a/src/client/s3proxy/core/ApiResult.ts +++ b/src/client/s3proxy/core/ApiResult.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/core/CancelablePromise.ts b/src/client/s3proxy/core/CancelablePromise.ts index 26ad303915af161f170cc144a5e7684be3053778..55fef8517238d2dab7478598eefca72e657b9fb5 100644 --- a/src/client/s3proxy/core/CancelablePromise.ts +++ b/src/client/s3proxy/core/CancelablePromise.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -22,15 +23,13 @@ export interface OnCancel { } export class CancelablePromise<T> implements Promise<T> { - readonly [Symbol.toStringTag]!: string; - - private _isResolved: boolean; - private _isRejected: boolean; - private _isCancelled: boolean; - private readonly _cancelHandlers: (() => void)[]; - private readonly _promise: Promise<T>; - private _resolve?: (value: T | PromiseLike<T>) => void; - private _reject?: (reason?: any) => void; + #isResolved: boolean; + #isRejected: boolean; + #isCancelled: boolean; + readonly #cancelHandlers: (() => void)[]; + readonly #promise: Promise<T>; + #resolve?: (value: T | PromiseLike<T>) => void; + #reject?: (reason?: any) => void; constructor( executor: ( @@ -39,78 +38,82 @@ export class CancelablePromise<T> implements Promise<T> { onCancel: OnCancel ) => void ) { - this._isResolved = false; - this._isRejected = false; - this._isCancelled = false; - this._cancelHandlers = []; - this._promise = new Promise<T>((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; + this.#isResolved = false; + this.#isRejected = false; + this.#isCancelled = false; + this.#cancelHandlers = []; + this.#promise = new Promise<T>((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; const onResolve = (value: T | PromiseLike<T>): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isResolved = true; - this._resolve?.(value); + this.#isResolved = true; + this.#resolve?.(value); }; const onReject = (reason?: any): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isRejected = true; - this._reject?.(reason); + this.#isRejected = true; + this.#reject?.(reason); }; const onCancel = (cancelHandler: () => void): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._cancelHandlers.push(cancelHandler); + this.#cancelHandlers.push(cancelHandler); }; Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this._isResolved, + get: (): boolean => this.#isResolved, }); Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this._isRejected, + get: (): boolean => this.#isRejected, }); Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this._isCancelled, + get: (): boolean => this.#isCancelled, }); return executor(onResolve, onReject, onCancel as OnCancel); }); } + get [Symbol.toStringTag]() { + return "Cancellable Promise"; + } + public then<TResult1 = T, TResult2 = never>( onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null ): Promise<TResult1 | TResult2> { - return this._promise.then(onFulfilled, onRejected); + return this.#promise.then(onFulfilled, onRejected); } public catch<TResult = never>( onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null ): Promise<T | TResult> { - return this._promise.catch(onRejected); + return this.#promise.catch(onRejected); } public finally(onFinally?: (() => void) | null): Promise<T> { - return this._promise.finally(onFinally); + return this.#promise.finally(onFinally); } public cancel(): void { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isCancelled = true; - if (this._cancelHandlers.length) { + this.#isCancelled = true; + if (this.#cancelHandlers.length) { try { - for (const cancelHandler of this._cancelHandlers) { + for (const cancelHandler of this.#cancelHandlers) { cancelHandler(); } } catch (error) { @@ -118,11 +121,11 @@ export class CancelablePromise<T> implements Promise<T> { return; } } - this._cancelHandlers.length = 0; - this._reject?.(new CancelError('Request aborted')); + this.#cancelHandlers.length = 0; + this.#reject?.(new CancelError('Request aborted')); } public get isCancelled(): boolean { - return this._isCancelled; + return this.#isCancelled; } } diff --git a/src/client/s3proxy/core/OpenAPI.ts b/src/client/s3proxy/core/OpenAPI.ts index 58ca8b0348f7e6038e827d3721a8fd5efe592205..f18fb9aeed82d0fd92b854566f9b61b5d519e1ca 100644 --- a/src/client/s3proxy/core/OpenAPI.ts +++ b/src/client/s3proxy/core/OpenAPI.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -11,11 +12,11 @@ export type OpenAPIConfig = { VERSION: string; WITH_CREDENTIALS: boolean; CREDENTIALS: 'include' | 'omit' | 'same-origin'; - TOKEN?: string | Resolver<string>; - USERNAME?: string | Resolver<string>; - PASSWORD?: string | Resolver<string>; - HEADERS?: Headers | Resolver<Headers>; - ENCODE_PATH?: (path: string) => string; + TOKEN?: string | Resolver<string> | undefined; + USERNAME?: string | Resolver<string> | undefined; + PASSWORD?: string | Resolver<string> | undefined; + HEADERS?: Headers | Resolver<Headers> | undefined; + ENCODE_PATH?: ((path: string) => string) | undefined; }; export const OpenAPI: OpenAPIConfig = { diff --git a/src/client/s3proxy/core/request.ts b/src/client/s3proxy/core/request.ts index 2b5375ab44ec20863646a5debca820b42c2b7500..1142d43219797c94e09f35ba83a9f5441892ddd2 100644 --- a/src/client/s3proxy/core/request.ts +++ b/src/client/s3proxy/core/request.ts @@ -1,8 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ import axios from 'axios'; -import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; +import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; import FormData from 'form-data'; import { ApiError } from './ApiError'; @@ -12,19 +13,19 @@ import { CancelablePromise } from './CancelablePromise'; import type { OnCancel } from './CancelablePromise'; import type { OpenAPIConfig } from './OpenAPI'; -const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { +export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { return value !== undefined && value !== null; }; -const isString = (value: any): value is string => { +export const isString = (value: any): value is string => { return typeof value === 'string'; }; -const isStringWithValue = (value: any): value is string => { +export const isStringWithValue = (value: any): value is string => { return isString(value) && value !== ''; }; -const isBlob = (value: any): value is Blob => { +export const isBlob = (value: any): value is Blob => { return ( typeof value === 'object' && typeof value.type === 'string' && @@ -37,15 +38,15 @@ const isBlob = (value: any): value is Blob => { ); }; -const isFormData = (value: any): value is FormData => { +export const isFormData = (value: any): value is FormData => { return value instanceof FormData; }; -const isSuccess = (status: number): boolean => { +export const isSuccess = (status: number): boolean => { return status >= 200 && status < 300; }; -const base64 = (str: string): string => { +export const base64 = (str: string): string => { try { return btoa(str); } catch (err) { @@ -54,7 +55,7 @@ const base64 = (str: string): string => { } }; -const getQueryString = (params: Record<string, any>): string => { +export const getQueryString = (params: Record<string, any>): string => { const qs: string[] = []; const append = (key: string, value: any) => { @@ -107,7 +108,7 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return url; }; -const getFormData = (options: ApiRequestOptions): FormData | undefined => { +export const getFormData = (options: ApiRequestOptions): FormData | undefined => { if (options.formData) { const formData = new FormData(); @@ -136,14 +137,14 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => { type Resolver<T> = (options: ApiRequestOptions) => Promise<T>; -const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { +export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { if (typeof resolver === 'function') { return (resolver as Resolver<T>)(options); } return resolver; }; -const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => { +export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => { const token = await resolve(options, config.TOKEN); const username = await resolve(options, config.USERNAME); const password = await resolve(options, config.PASSWORD); @@ -186,21 +187,22 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for return headers; }; -const getRequestBody = (options: ApiRequestOptions): any => { +export const getRequestBody = (options: ApiRequestOptions): any => { if (options.body) { return options.body; } return undefined; }; -const sendRequest = async <T>( +export const sendRequest = async <T>( config: OpenAPIConfig, options: ApiRequestOptions, url: string, body: any, formData: FormData | undefined, headers: Record<string, string>, - onCancel: OnCancel + onCancel: OnCancel, + axiosClient: AxiosInstance ): Promise<AxiosResponse<T>> => { const source = axios.CancelToken.source(); @@ -216,7 +218,7 @@ const sendRequest = async <T>( onCancel(() => source.cancel('The user aborted a request.')); try { - return await axios.request(requestConfig); + return await axiosClient.request(requestConfig); } catch (error) { const axiosError = error as AxiosError<T>; if (axiosError.response) { @@ -226,7 +228,7 @@ const sendRequest = async <T>( } }; -const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => { +export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => { if (responseHeader) { const content = response.headers[responseHeader]; if (isString(content)) { @@ -236,14 +238,14 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string return undefined; }; -const getResponseBody = (response: AxiosResponse<any>): any => { +export const getResponseBody = (response: AxiosResponse<any>): any => { if (response.status !== 204) { return response.data; } return undefined; }; -const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { +export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { const errors: Record<number, string> = { 400: 'Bad Request', 401: 'Unauthorized', @@ -261,7 +263,19 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => } if (!result.ok) { - throw new ApiError(options, result, 'Generic Error'); + const errorStatus = result.status ?? 'unknown'; + const errorStatusText = result.statusText ?? 'unknown'; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return undefined; + } + })(); + + throw new ApiError(options, result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + ); } }; @@ -269,10 +283,11 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => * Request method * @param config The OpenAPI configuration object * @param options The request options from the service + * @param axiosClient The axios client instance to use * @returns CancelablePromise<T> * @throws ApiError */ -export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => { +export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => { return new CancelablePromise(async (resolve, reject, onCancel) => { try { const url = getUrl(config, options); @@ -281,7 +296,7 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options, formData); if (!onCancel.isCancelled) { - const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel); + const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient); const responseBody = getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/client/s3proxy/index.ts b/src/client/s3proxy/index.ts index 3006a77d6c0beb2084eef7aedd5d7ddca05702e5..0748f5b918bfb1cd83e3fe4c546af20bca1caeff 100644 --- a/src/client/s3proxy/index.ts +++ b/src/client/s3proxy/index.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/models/BucketIn.ts b/src/client/s3proxy/models/BucketIn.ts index 530031e6c9db2eed482ac3b2a05f512112fdcb3e..c088c1be4e4944e95f6bcc400ba85ea03c59ad91 100644 --- a/src/client/s3proxy/models/BucketIn.ts +++ b/src/client/s3proxy/models/BucketIn.ts @@ -1,9 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ /** + * * Schema for creating a new bucket. + * */ export type BucketIn = { /** diff --git a/src/client/s3proxy/models/BucketOut.ts b/src/client/s3proxy/models/BucketOut.ts index 5fd172a904ae83eab5401c23b30ed9fef8ac18a3..20e3d57428c53597a75151beb4f0b739f038fed3 100644 --- a/src/client/s3proxy/models/BucketOut.ts +++ b/src/client/s3proxy/models/BucketOut.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -5,7 +6,9 @@ import type { Constraint } from './Constraint'; /** + * * Schema for answering a request with a bucket. + * */ export type BucketOut = { /** @@ -17,9 +20,9 @@ export type BucketOut = { */ description: string; /** - * Time when the bucket was created + * Time when the bucket was created as UNIX timestamp */ - created_at: string; + created_at: number; /** * UID of the owner */ @@ -35,6 +38,6 @@ export type BucketOut = { /** * Constraint for the owner of the bucket */ - owner_constraint?: Constraint; + owner_constraint: (Constraint | null); }; diff --git a/src/client/s3proxy/models/BucketPermissionIn.ts b/src/client/s3proxy/models/BucketPermissionIn.ts index 315a6a5f8ebc4820470a4ebcd5f7503d0b52065a..06bd223c69ce58189c1699343b1018187a8942c1 100644 --- a/src/client/s3proxy/models/BucketPermissionIn.ts +++ b/src/client/s3proxy/models/BucketPermissionIn.ts @@ -1,25 +1,23 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ import type { Permission } from './Permission'; -/** - * Schema for the parameters of a bucket permission. - */ export type BucketPermissionIn = { /** - * Start date of permission + * Start date of permission as UNIX timestamp */ - from_timestamp?: string; + from_timestamp?: (number | null); /** - * End date of permission + * End date of permission as UNIX timestamp */ - to_timestamp?: string; + to_timestamp?: (number | null); /** * Prefix of subfolder */ - file_prefix?: string; + file_prefix?: (string | null); /** * Permission */ diff --git a/src/client/s3proxy/models/BucketPermissionOut.ts b/src/client/s3proxy/models/BucketPermissionOut.ts index 86c6b26bc3b94e97b75ad219affb1dfe38509b3b..dfa89530c878b0dbff5f9a62e0e1a9034f210729 100644 --- a/src/client/s3proxy/models/BucketPermissionOut.ts +++ b/src/client/s3proxy/models/BucketPermissionOut.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -5,25 +6,27 @@ import type { Permission } from './Permission'; /** + * * Schema for the bucket permissions. + * */ export type BucketPermissionOut = { /** - * Start date of permission + * Start date of permission as UNIX timestamp */ - from_timestamp?: string; + from_timestamp: (number | null); /** - * End date of permission + * End date of permission as UNIX timestamp */ - to_timestamp?: string; + to_timestamp: (number | null); /** * Prefix of subfolder */ - file_prefix?: string; + file_prefix: (string | null); /** * Permission */ - permission?: (Permission | string); + permission: (Permission | string); /** * UID of the grantee */ diff --git a/src/client/s3proxy/models/BucketPermissionParameters.ts b/src/client/s3proxy/models/BucketPermissionParameters.ts index 3317e6a70362dfaf2f390490ad641dac0c81ec64..6eca4a9543e251adc45c1a109124e22024e6f585 100644 --- a/src/client/s3proxy/models/BucketPermissionParameters.ts +++ b/src/client/s3proxy/models/BucketPermissionParameters.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -5,21 +6,23 @@ import type { Permission } from './Permission'; /** + * * Schema for the parameters of a bucket permission. + * */ export type BucketPermissionParameters = { /** - * Start date of permission + * Start date of permission as UNIX timestamp */ - from_timestamp?: string; + from_timestamp?: (number | null); /** - * End date of permission + * End date of permission as UNIX timestamp */ - to_timestamp?: string; + to_timestamp?: (number | null); /** * Prefix of subfolder */ - file_prefix?: string; + file_prefix?: (string | null); /** * Permission */ diff --git a/src/client/s3proxy/models/BucketType.ts b/src/client/s3proxy/models/BucketType.ts index ba0c539062b0cda4b04349e01c229f3bb4c1c0dc..b096c70b0b7234bc57dd1ecc2223bd41aa3e0d08 100644 --- a/src/client/s3proxy/models/BucketType.ts +++ b/src/client/s3proxy/models/BucketType.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/models/Constraint.ts b/src/client/s3proxy/models/Constraint.ts index 2eb7b569f9ee51ac37791185db5cacc6949af245..afa995a983b5e08b66d55b8dfa4243553351d3a7 100644 --- a/src/client/s3proxy/models/Constraint.ts +++ b/src/client/s3proxy/models/Constraint.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/models/ErrorDetail.ts b/src/client/s3proxy/models/ErrorDetail.ts index b01e5aa28734c901620e98f8795e9528004d1513..343c8124f88bce59decea9cba6085126fe143db8 100644 --- a/src/client/s3proxy/models/ErrorDetail.ts +++ b/src/client/s3proxy/models/ErrorDetail.ts @@ -1,9 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ /** + * * Schema for a error due to a rejected request. + * */ export type ErrorDetail = { /** diff --git a/src/client/s3proxy/models/HTTPValidationError.ts b/src/client/s3proxy/models/HTTPValidationError.ts index a8c21354123500873c7548194a36421eb8316c78..c0bcc87cf7f3222638466bc2e5753ca1accf01f7 100644 --- a/src/client/s3proxy/models/HTTPValidationError.ts +++ b/src/client/s3proxy/models/HTTPValidationError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/models/Permission.ts b/src/client/s3proxy/models/Permission.ts index cacede746c4a5a681c274299b195e373c5616d43..00592a496a01e178e7192ba3bd76c51b29a3a138 100644 --- a/src/client/s3proxy/models/Permission.ts +++ b/src/client/s3proxy/models/Permission.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/models/PermissionStatus.ts b/src/client/s3proxy/models/PermissionStatus.ts index fb8a55251c5b311395d815d829faff37a73bcac1..d51e0928ec89e662e033c833ceacd5bd6c66bc74 100644 --- a/src/client/s3proxy/models/PermissionStatus.ts +++ b/src/client/s3proxy/models/PermissionStatus.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/models/S3Key.ts b/src/client/s3proxy/models/S3Key.ts index 5a1aa255ea54281fb45ae5a32d09d459ef1bac29..bcdec338b5948f9b78c53f7721dd42f8ca12bcdd 100644 --- a/src/client/s3proxy/models/S3Key.ts +++ b/src/client/s3proxy/models/S3Key.ts @@ -1,9 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ /** + * * Schema for a S3 key associated with a user. + * */ export type S3Key = { /** diff --git a/src/client/s3proxy/models/S3ObjectMetaInformation.ts b/src/client/s3proxy/models/S3ObjectMetaInformation.ts index ed92a733014053f1c91d4ac7f4067271fb8b6904..8d53cecc418f9ebdadc7c43f6eaea19da292a0f0 100644 --- a/src/client/s3proxy/models/S3ObjectMetaInformation.ts +++ b/src/client/s3proxy/models/S3ObjectMetaInformation.ts @@ -1,9 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ /** + * * Schema for the meta-information about a S3 object. + * */ export type S3ObjectMetaInformation = { /** diff --git a/src/client/s3proxy/models/ValidationError.ts b/src/client/s3proxy/models/ValidationError.ts index 16dc86a6807d9ad1f7e9d06638d4bd002682dc42..18997ec72f4103731f38d915508522ba23ba8506 100644 --- a/src/client/s3proxy/models/ValidationError.ts +++ b/src/client/s3proxy/models/ValidationError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/s3proxy/services/BucketPermissionService.ts b/src/client/s3proxy/services/BucketPermissionService.ts index ca7cba59dbb8af1dbb3f88bd9b0e3d125f2a148c..365cf2ee156716b11099883696bbb7d52bda3146 100644 --- a/src/client/s3proxy/services/BucketPermissionService.ts +++ b/src/client/s3proxy/services/BucketPermissionService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -47,31 +48,29 @@ export class BucketPermissionService { } /** - * Update a bucket permission - * Update a permission for a bucket and user. + * Delete a bucket permission + * Delete the bucket permissions for the specific combination of bucket and user. * - * Permission "bucket_permission:read" required if current user is the target the bucket permission, - * otherwise "bucket_permission:update" required. - * @param uid UID of a user + * The owner of the bucket and the grantee of the permission can delete it. + * + * Permission "bucket_permission:delete" required if current user is the target or owner of the bucket permission, + * otherwise "bucket_permission:delete_any" required. * @param bucketName Name of bucket - * @param requestBody - * @returns BucketPermissionOut Successful Response + * @param uid UID of a user + * @returns void * @throws ApiError */ - public static bucketPermissionUpdatePermission( - uid: string, + public static bucketPermissionDeletePermissionForBucket( bucketName: string, - requestBody: BucketPermissionParameters, - ): CancelablePromise<BucketPermissionOut> { + uid: string, + ): CancelablePromise<void> { return __request(OpenAPI, { - method: 'PUT', + method: 'DELETE', url: '/permissions/bucket/{bucket_name}/user/{uid}', path: { - 'uid': uid, 'bucket_name': bucketName, + 'uid': uid, }, - body: requestBody, - mediaType: 'application/json', errors: { 400: `Error decoding JWT Token`, 403: `Not authenticated`, @@ -82,29 +81,31 @@ export class BucketPermissionService { } /** - * Delete a bucket permission - * Delete the bucket permissions for the specific combination of bucket and user. - * - * The owner of the bucket and the grantee of the permission can delete it. + * Update a bucket permission + * Update a permission for a bucket and user. * - * Permission "bucket_permission:delete" required if current user is the target or owner of the bucket permission, - * otherwise "bucket_permission:delete_any" required. + * Permission "bucket_permission:read" required if current user is the target the bucket permission, + * otherwise "bucket_permission:update" required. * @param bucketName Name of bucket * @param uid UID of a user - * @returns void + * @param requestBody + * @returns BucketPermissionOut Successful Response * @throws ApiError */ - public static bucketPermissionDeletePermissionForBucket( + public static bucketPermissionUpdatePermission( bucketName: string, uid: string, - ): CancelablePromise<void> { + requestBody: BucketPermissionParameters, + ): CancelablePromise<BucketPermissionOut> { return __request(OpenAPI, { - method: 'DELETE', + method: 'PUT', url: '/permissions/bucket/{bucket_name}/user/{uid}', path: { 'bucket_name': bucketName, 'uid': uid, }, + body: requestBody, + mediaType: 'application/json', errors: { 400: `Error decoding JWT Token`, 403: `Not authenticated`, @@ -128,8 +129,8 @@ export class BucketPermissionService { */ public static bucketPermissionListPermissionsPerBucket( bucketName: string, - permissionType?: Array<Permission>, - permissionStatus?: PermissionStatus, + permissionType?: (Array<Permission> | null), + permissionStatus?: (PermissionStatus | null), ): CancelablePromise<Array<BucketPermissionOut>> { return __request(OpenAPI, { method: 'GET', @@ -164,8 +165,8 @@ export class BucketPermissionService { */ public static bucketPermissionListPermissionsPerUser( uid: string, - permissionType?: Array<Permission>, - permissionStatus?: PermissionStatus, + permissionType?: (Array<Permission> | null), + permissionStatus?: (PermissionStatus | null), ): CancelablePromise<Array<BucketPermissionOut>> { return __request(OpenAPI, { method: 'GET', diff --git a/src/client/s3proxy/services/BucketService.ts b/src/client/s3proxy/services/BucketService.ts index a56c3ccf0b37e5f164d1ae1eae0e562ce559e5c0..20d718fb164bcfa06c2102633687ee381d6d37ea 100644 --- a/src/client/s3proxy/services/BucketService.ts +++ b/src/client/s3proxy/services/BucketService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -23,7 +24,7 @@ export class BucketService { * @throws ApiError */ public static bucketListBuckets( - user?: string, + user?: (string | null), bucketType?: BucketType, ): CancelablePromise<Array<BucketOut>> { return __request(OpenAPI, { @@ -134,6 +135,7 @@ export class BucketService { } /** + * @deprecated * Get the metadata of the objects in the bucket * Get the metadata of the objects in the bucket. * @@ -149,7 +151,7 @@ export class BucketService { */ public static objectGetBucketObjects( bucketName: string, - filePrefix?: string, + filePrefix?: (string | null), ): CancelablePromise<Array<S3ObjectMetaInformation>> { return __request(OpenAPI, { method: 'GET', @@ -170,6 +172,7 @@ export class BucketService { } /** + * @deprecated * Get the metadata about a specific object * Get the metadata of a specific object in a bucket. * diff --git a/src/client/s3proxy/services/ObjectService.ts b/src/client/s3proxy/services/ObjectService.ts index ae0716a96fa8b8709d0b730ab04a6d47a6e5cf51..0df2a8345aa3fdba1c99846f934f3035660a5c72 100644 --- a/src/client/s3proxy/services/ObjectService.ts +++ b/src/client/s3proxy/services/ObjectService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -10,6 +11,7 @@ import { request as __request } from '../core/request'; export class ObjectService { /** + * @deprecated * Get the metadata of the objects in the bucket * Get the metadata of the objects in the bucket. * @@ -25,7 +27,7 @@ export class ObjectService { */ public static objectGetBucketObjects( bucketName: string, - filePrefix?: string, + filePrefix?: (string | null), ): CancelablePromise<Array<S3ObjectMetaInformation>> { return __request(OpenAPI, { method: 'GET', @@ -46,6 +48,7 @@ export class ObjectService { } /** + * @deprecated * Get the metadata about a specific object * Get the metadata of a specific object in a bucket. * diff --git a/src/client/s3proxy/services/S3KeyService.ts b/src/client/s3proxy/services/S3KeyService.ts index 5d052a7927e3d4f2ba643fe31515856580e2406b..c8e048bdc22fbbd683259c5850045063938a7aee 100644 --- a/src/client/s3proxy/services/S3KeyService.ts +++ b/src/client/s3proxy/services/S3KeyService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/core/ApiError.ts b/src/client/workflow/core/ApiError.ts index 99d792996765118614adf6a232bc8766b95710e9..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648 100644 --- a/src/client/workflow/core/ApiError.ts +++ b/src/client/workflow/core/ApiError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/core/ApiRequestOptions.ts b/src/client/workflow/core/ApiRequestOptions.ts index c7b77538c5ce6aa48f141541392121fbcafc4821..c19adcc94dc5f015865368d6d64751de5c243aa3 100644 --- a/src/client/workflow/core/ApiRequestOptions.ts +++ b/src/client/workflow/core/ApiRequestOptions.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/core/ApiResult.ts b/src/client/workflow/core/ApiResult.ts index b095dc7708af2aa405a6dc7ad08f423334bc7f24..ad8fef2bc334c2aedf5c043896fd394229453ff0 100644 --- a/src/client/workflow/core/ApiResult.ts +++ b/src/client/workflow/core/ApiResult.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/core/CancelablePromise.ts b/src/client/workflow/core/CancelablePromise.ts index 26ad303915af161f170cc144a5e7684be3053778..55fef8517238d2dab7478598eefca72e657b9fb5 100644 --- a/src/client/workflow/core/CancelablePromise.ts +++ b/src/client/workflow/core/CancelablePromise.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -22,15 +23,13 @@ export interface OnCancel { } export class CancelablePromise<T> implements Promise<T> { - readonly [Symbol.toStringTag]!: string; - - private _isResolved: boolean; - private _isRejected: boolean; - private _isCancelled: boolean; - private readonly _cancelHandlers: (() => void)[]; - private readonly _promise: Promise<T>; - private _resolve?: (value: T | PromiseLike<T>) => void; - private _reject?: (reason?: any) => void; + #isResolved: boolean; + #isRejected: boolean; + #isCancelled: boolean; + readonly #cancelHandlers: (() => void)[]; + readonly #promise: Promise<T>; + #resolve?: (value: T | PromiseLike<T>) => void; + #reject?: (reason?: any) => void; constructor( executor: ( @@ -39,78 +38,82 @@ export class CancelablePromise<T> implements Promise<T> { onCancel: OnCancel ) => void ) { - this._isResolved = false; - this._isRejected = false; - this._isCancelled = false; - this._cancelHandlers = []; - this._promise = new Promise<T>((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; + this.#isResolved = false; + this.#isRejected = false; + this.#isCancelled = false; + this.#cancelHandlers = []; + this.#promise = new Promise<T>((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; const onResolve = (value: T | PromiseLike<T>): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isResolved = true; - this._resolve?.(value); + this.#isResolved = true; + this.#resolve?.(value); }; const onReject = (reason?: any): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isRejected = true; - this._reject?.(reason); + this.#isRejected = true; + this.#reject?.(reason); }; const onCancel = (cancelHandler: () => void): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._cancelHandlers.push(cancelHandler); + this.#cancelHandlers.push(cancelHandler); }; Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this._isResolved, + get: (): boolean => this.#isResolved, }); Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this._isRejected, + get: (): boolean => this.#isRejected, }); Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this._isCancelled, + get: (): boolean => this.#isCancelled, }); return executor(onResolve, onReject, onCancel as OnCancel); }); } + get [Symbol.toStringTag]() { + return "Cancellable Promise"; + } + public then<TResult1 = T, TResult2 = never>( onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null ): Promise<TResult1 | TResult2> { - return this._promise.then(onFulfilled, onRejected); + return this.#promise.then(onFulfilled, onRejected); } public catch<TResult = never>( onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null ): Promise<T | TResult> { - return this._promise.catch(onRejected); + return this.#promise.catch(onRejected); } public finally(onFinally?: (() => void) | null): Promise<T> { - return this._promise.finally(onFinally); + return this.#promise.finally(onFinally); } public cancel(): void { - if (this._isResolved || this._isRejected || this._isCancelled) { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { return; } - this._isCancelled = true; - if (this._cancelHandlers.length) { + this.#isCancelled = true; + if (this.#cancelHandlers.length) { try { - for (const cancelHandler of this._cancelHandlers) { + for (const cancelHandler of this.#cancelHandlers) { cancelHandler(); } } catch (error) { @@ -118,11 +121,11 @@ export class CancelablePromise<T> implements Promise<T> { return; } } - this._cancelHandlers.length = 0; - this._reject?.(new CancelError('Request aborted')); + this.#cancelHandlers.length = 0; + this.#reject?.(new CancelError('Request aborted')); } public get isCancelled(): boolean { - return this._isCancelled; + return this.#isCancelled; } } diff --git a/src/client/workflow/core/OpenAPI.ts b/src/client/workflow/core/OpenAPI.ts index 809eda09cde31266ee99deeec2e0b53159c5a8e7..af264be25a464af3a2b3dcc57232c87766ac0180 100644 --- a/src/client/workflow/core/OpenAPI.ts +++ b/src/client/workflow/core/OpenAPI.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -11,11 +12,11 @@ export type OpenAPIConfig = { VERSION: string; WITH_CREDENTIALS: boolean; CREDENTIALS: 'include' | 'omit' | 'same-origin'; - TOKEN?: string | Resolver<string>; - USERNAME?: string | Resolver<string>; - PASSWORD?: string | Resolver<string>; - HEADERS?: Headers | Resolver<Headers>; - ENCODE_PATH?: (path: string) => string; + TOKEN?: string | Resolver<string> | undefined; + USERNAME?: string | Resolver<string> | undefined; + PASSWORD?: string | Resolver<string> | undefined; + HEADERS?: Headers | Resolver<Headers> | undefined; + ENCODE_PATH?: ((path: string) => string) | undefined; }; export const OpenAPI: OpenAPIConfig = { diff --git a/src/client/workflow/core/request.ts b/src/client/workflow/core/request.ts index 2b5375ab44ec20863646a5debca820b42c2b7500..1142d43219797c94e09f35ba83a9f5441892ddd2 100644 --- a/src/client/workflow/core/request.ts +++ b/src/client/workflow/core/request.ts @@ -1,8 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ import axios from 'axios'; -import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; +import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; import FormData from 'form-data'; import { ApiError } from './ApiError'; @@ -12,19 +13,19 @@ import { CancelablePromise } from './CancelablePromise'; import type { OnCancel } from './CancelablePromise'; import type { OpenAPIConfig } from './OpenAPI'; -const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { +export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { return value !== undefined && value !== null; }; -const isString = (value: any): value is string => { +export const isString = (value: any): value is string => { return typeof value === 'string'; }; -const isStringWithValue = (value: any): value is string => { +export const isStringWithValue = (value: any): value is string => { return isString(value) && value !== ''; }; -const isBlob = (value: any): value is Blob => { +export const isBlob = (value: any): value is Blob => { return ( typeof value === 'object' && typeof value.type === 'string' && @@ -37,15 +38,15 @@ const isBlob = (value: any): value is Blob => { ); }; -const isFormData = (value: any): value is FormData => { +export const isFormData = (value: any): value is FormData => { return value instanceof FormData; }; -const isSuccess = (status: number): boolean => { +export const isSuccess = (status: number): boolean => { return status >= 200 && status < 300; }; -const base64 = (str: string): string => { +export const base64 = (str: string): string => { try { return btoa(str); } catch (err) { @@ -54,7 +55,7 @@ const base64 = (str: string): string => { } }; -const getQueryString = (params: Record<string, any>): string => { +export const getQueryString = (params: Record<string, any>): string => { const qs: string[] = []; const append = (key: string, value: any) => { @@ -107,7 +108,7 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return url; }; -const getFormData = (options: ApiRequestOptions): FormData | undefined => { +export const getFormData = (options: ApiRequestOptions): FormData | undefined => { if (options.formData) { const formData = new FormData(); @@ -136,14 +137,14 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => { type Resolver<T> = (options: ApiRequestOptions) => Promise<T>; -const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { +export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { if (typeof resolver === 'function') { return (resolver as Resolver<T>)(options); } return resolver; }; -const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => { +export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => { const token = await resolve(options, config.TOKEN); const username = await resolve(options, config.USERNAME); const password = await resolve(options, config.PASSWORD); @@ -186,21 +187,22 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for return headers; }; -const getRequestBody = (options: ApiRequestOptions): any => { +export const getRequestBody = (options: ApiRequestOptions): any => { if (options.body) { return options.body; } return undefined; }; -const sendRequest = async <T>( +export const sendRequest = async <T>( config: OpenAPIConfig, options: ApiRequestOptions, url: string, body: any, formData: FormData | undefined, headers: Record<string, string>, - onCancel: OnCancel + onCancel: OnCancel, + axiosClient: AxiosInstance ): Promise<AxiosResponse<T>> => { const source = axios.CancelToken.source(); @@ -216,7 +218,7 @@ const sendRequest = async <T>( onCancel(() => source.cancel('The user aborted a request.')); try { - return await axios.request(requestConfig); + return await axiosClient.request(requestConfig); } catch (error) { const axiosError = error as AxiosError<T>; if (axiosError.response) { @@ -226,7 +228,7 @@ const sendRequest = async <T>( } }; -const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => { +export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => { if (responseHeader) { const content = response.headers[responseHeader]; if (isString(content)) { @@ -236,14 +238,14 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string return undefined; }; -const getResponseBody = (response: AxiosResponse<any>): any => { +export const getResponseBody = (response: AxiosResponse<any>): any => { if (response.status !== 204) { return response.data; } return undefined; }; -const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { +export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { const errors: Record<number, string> = { 400: 'Bad Request', 401: 'Unauthorized', @@ -261,7 +263,19 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => } if (!result.ok) { - throw new ApiError(options, result, 'Generic Error'); + const errorStatus = result.status ?? 'unknown'; + const errorStatusText = result.statusText ?? 'unknown'; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return undefined; + } + })(); + + throw new ApiError(options, result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + ); } }; @@ -269,10 +283,11 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => * Request method * @param config The OpenAPI configuration object * @param options The request options from the service + * @param axiosClient The axios client instance to use * @returns CancelablePromise<T> * @throws ApiError */ -export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => { +export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => { return new CancelablePromise(async (resolve, reject, onCancel) => { try { const url = getUrl(config, options); @@ -281,7 +296,7 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options, formData); if (!onCancel.isCancelled) { - const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel); + const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient); const responseBody = getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/client/workflow/index.ts b/src/client/workflow/index.ts index cb6c872427f3cb7150fc2c4688b974cc40e20c42..3b82994fee1718758b8ac10abf07efc931577860 100644 --- a/src/client/workflow/index.ts +++ b/src/client/workflow/index.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -6,22 +7,30 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise'; export { OpenAPI } from './core/OpenAPI'; export type { OpenAPIConfig } from './core/OpenAPI'; -export type { Body_Workflow_create_workflow } from './models/Body_Workflow_create_workflow'; -export type { Body_Workflow_update_workflow } from './models/Body_Workflow_update_workflow'; +export type { Body_Workflow_Version_upload_workflow_version_icon } from './models/Body_Workflow_Version_upload_workflow_version_icon'; export type { DevWorkflowExecutionIn } from './models/DevWorkflowExecutionIn'; +export { DocumentationEnum } from './models/DocumentationEnum'; export type { ErrorDetail } from './models/ErrorDetail'; export type { HTTPValidationError } from './models/HTTPValidationError'; +export type { IconUpdateOut } from './models/IconUpdateOut'; export { Status } from './models/Status'; export type { ValidationError } from './models/ValidationError'; +export type { WorkflowCredentialsIn } from './models/WorkflowCredentialsIn'; +export type { WorkflowCredentialsOut } from './models/WorkflowCredentialsOut'; export type { WorkflowExecutionIn } from './models/WorkflowExecutionIn'; export type { WorkflowExecutionOut } from './models/WorkflowExecutionOut'; export { WorkflowExecutionStatus } from './models/WorkflowExecutionStatus'; +export type { WorkflowIn } from './models/WorkflowIn'; +export type { WorkflowModeIn } from './models/WorkflowModeIn'; +export type { WorkflowModeOut } from './models/WorkflowModeOut'; export type { WorkflowOut } from './models/WorkflowOut'; export type { WorkflowStatistic } from './models/WorkflowStatistic'; -export type { WorkflowVersionFull } from './models/WorkflowVersionFull'; -export type { WorkflowVersionReduced } from './models/WorkflowVersionReduced'; +export type { WorkflowUpdate } from './models/WorkflowUpdate'; +export type { WorkflowVersion } from './models/WorkflowVersion'; export type { WorkflowVersionStatus } from './models/WorkflowVersionStatus'; export { WorkflowService } from './services/WorkflowService'; +export { WorkflowCredentialsService } from './services/WorkflowCredentialsService'; export { WorkflowExecutionService } from './services/WorkflowExecutionService'; +export { WorkflowModeService } from './services/WorkflowModeService'; export { WorkflowVersionService } from './services/WorkflowVersionService'; diff --git a/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts b/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts new file mode 100644 index 0000000000000000000000000000000000000000..208b0c24d5b34514fe1156aafeddabd72645a53c --- /dev/null +++ b/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Body_Workflow_Version_upload_workflow_version_icon = { + /** + * Optional Icon for the Workflow. + */ + icon: Blob; +}; + diff --git a/src/client/workflow/models/Body_Workflow_create_workflow.ts b/src/client/workflow/models/Body_Workflow_create_workflow.ts deleted file mode 100644 index f5e33b0c935c1cc3e22c08ffe3a205b2c4dc441e..0000000000000000000000000000000000000000 --- a/src/client/workflow/models/Body_Workflow_create_workflow.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type Body_Workflow_create_workflow = { - /** - * Optional Icon for the Workflow. - */ - icon?: Blob; - name: string; - short_description: string; - repository_url: string; - git_commit_hash: string; - initial_version?: string; -}; - diff --git a/src/client/workflow/models/Body_Workflow_update_workflow.ts b/src/client/workflow/models/Body_Workflow_update_workflow.ts deleted file mode 100644 index 9c0abe315adff9adeb2990f6eb856be6e415f1ef..0000000000000000000000000000000000000000 --- a/src/client/workflow/models/Body_Workflow_update_workflow.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type Body_Workflow_update_workflow = { - /** - * Optional Icon for the workflow version. If None, then the previous one will be reused. - */ - icon?: Blob; - version: string; - git_commit_hash: string; -}; - diff --git a/src/client/workflow/models/DevWorkflowExecutionIn.ts b/src/client/workflow/models/DevWorkflowExecutionIn.ts index b48bb736dfaa6897fe4dfba88b18b7f22645db24..7e69f9c33628b53f763c5a299d320e892c4ec4d9 100644 --- a/src/client/workflow/models/DevWorkflowExecutionIn.ts +++ b/src/client/workflow/models/DevWorkflowExecutionIn.ts @@ -1,16 +1,19 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { WorkflowModeIn } from './WorkflowModeIn'; + export type DevWorkflowExecutionIn = { /** * Parameters for this workflow */ - parameters: any; + parameters: Record<string, any>; /** * Bucket where to save the Nextflow report. If None, no report will be generated */ - report_output_bucket?: string; + report_output_bucket?: (string | null); /** * Hash of the git commit */ @@ -19,5 +22,13 @@ export type DevWorkflowExecutionIn = { * URL to the Git repository belonging to this workflow */ repository_url: string; + /** + * Token to access the content git repository + */ + token?: (string | null); + /** + * Mode of the workflow with an alternative entrypoint + */ + mode?: (WorkflowModeIn | null); }; diff --git a/src/client/workflow/models/DocumentationEnum.ts b/src/client/workflow/models/DocumentationEnum.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ae04856a01b1b0b6dfe8c550ec97720aaeb4cd6 --- /dev/null +++ b/src/client/workflow/models/DocumentationEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export enum DocumentationEnum { + USAGE = 'usage', + INPUT = 'input', + OUTPUT = 'output', + CHANGELOG = 'changelog', + PARAMETER_SCHEMA = 'parameter_schema', +} diff --git a/src/client/workflow/models/ErrorDetail.ts b/src/client/workflow/models/ErrorDetail.ts index b01e5aa28734c901620e98f8795e9528004d1513..eba50ab9335b70df9b29cada4c54cf9ffaad10a4 100644 --- a/src/client/workflow/models/ErrorDetail.ts +++ b/src/client/workflow/models/ErrorDetail.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/models/HTTPValidationError.ts b/src/client/workflow/models/HTTPValidationError.ts index a8c21354123500873c7548194a36421eb8316c78..c0bcc87cf7f3222638466bc2e5753ca1accf01f7 100644 --- a/src/client/workflow/models/HTTPValidationError.ts +++ b/src/client/workflow/models/HTTPValidationError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/models/IconUpdateOut.ts b/src/client/workflow/models/IconUpdateOut.ts new file mode 100644 index 0000000000000000000000000000000000000000..0499796ef14433d99fae156f3508df5ed0db9984 --- /dev/null +++ b/src/client/workflow/models/IconUpdateOut.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type IconUpdateOut = { + /** + * URL to the uploaded icon + */ + icon_url: string; +}; + diff --git a/src/client/workflow/models/Status.ts b/src/client/workflow/models/Status.ts index 6629a4080e6eb4520e1c54f0219955484d30a2ee..8da66ea5e286d86b0f4d9d1c3b7bac8167d769ab 100644 --- a/src/client/workflow/models/Status.ts +++ b/src/client/workflow/models/Status.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/models/ValidationError.ts b/src/client/workflow/models/ValidationError.ts index 16dc86a6807d9ad1f7e9d06638d4bd002682dc42..18997ec72f4103731f38d915508522ba23ba8506 100644 --- a/src/client/workflow/models/ValidationError.ts +++ b/src/client/workflow/models/ValidationError.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/models/WorkflowCredentialsIn.ts b/src/client/workflow/models/WorkflowCredentialsIn.ts new file mode 100644 index 0000000000000000000000000000000000000000..37723a3e3a9384f04e6d52ecd6ac4f302d6ac304 --- /dev/null +++ b/src/client/workflow/models/WorkflowCredentialsIn.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type WorkflowCredentialsIn = { + /** + * Token to access the content git repository + */ + token: string; +}; + diff --git a/src/client/workflow/models/WorkflowCredentialsOut.ts b/src/client/workflow/models/WorkflowCredentialsOut.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b669c25db50ce191e19e90ff62ce1b18a0447c3 --- /dev/null +++ b/src/client/workflow/models/WorkflowCredentialsOut.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type WorkflowCredentialsOut = { + /** + * Token to access the content git repository + */ + token: (string | null); +}; + diff --git a/src/client/workflow/models/WorkflowExecutionIn.ts b/src/client/workflow/models/WorkflowExecutionIn.ts index 60d3b37c8c6c90f304fe8ff1fed86e9adddd3731..e61ac3d63f55e2f48c7076f73fc3e5dc9764c99c 100644 --- a/src/client/workflow/models/WorkflowExecutionIn.ts +++ b/src/client/workflow/models/WorkflowExecutionIn.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -10,14 +11,18 @@ export type WorkflowExecutionIn = { /** * Optional notes for this workflow execution */ - notes?: string; + notes?: (string | null); + /** + * ID of the workflow mode this workflow execution runs in + */ + mode?: (string | null); /** * Parameters for this workflow */ - parameters: any; + parameters: Record<string, any>; /** - * Bucket where to save the Nextflow report. If None, no report will be generated. With our without prefix 's3://' + * Bucket where to save the Nextflow report. If None, no report will be generated. With or without prefix 's3://' */ - report_output_bucket?: string; + report_output_bucket?: (string | null); }; diff --git a/src/client/workflow/models/WorkflowExecutionOut.ts b/src/client/workflow/models/WorkflowExecutionOut.ts index 8561ab6dba292e57cb31a52c74997303d1e34ac5..9532a9c0841cf85cc587abd91367e6755bdf8596 100644 --- a/src/client/workflow/models/WorkflowExecutionOut.ts +++ b/src/client/workflow/models/WorkflowExecutionOut.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -8,11 +9,15 @@ export type WorkflowExecutionOut = { /** * Workflow version git commit hash */ - workflow_version_id: string; + workflow_version_id: (string | null); /** * Optional notes for this workflow execution */ - notes?: string; + notes: (string | null); + /** + * ID of the workflow mode this workflow execution runs in + */ + mode: (string | null); /** * ID of the workflow execution */ @@ -22,13 +27,13 @@ export type WorkflowExecutionOut = { */ user_id: string; /** - * Start time of the workflow execution + * Start time of the workflow execution as UNIX timestamp */ - start_time: string; + start_time: number; /** - * End time of the workflow execution + * End time of the workflow execution as UNIX timestamp */ - end_time?: string; + end_time: (number | null); /** * Status of the workflow execution */ @@ -36,6 +41,6 @@ export type WorkflowExecutionOut = { /** * Id of the workflow */ - workflow_id: string; + workflow_id: (string | null); }; diff --git a/src/client/workflow/models/WorkflowExecutionStatus.ts b/src/client/workflow/models/WorkflowExecutionStatus.ts index 9b090d159dca2ac1bfe959eb6bcff35193748088..dee8c8fea4f0bbb84f2b8aaf69f626989e1639a6 100644 --- a/src/client/workflow/models/WorkflowExecutionStatus.ts +++ b/src/client/workflow/models/WorkflowExecutionStatus.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/models/WorkflowIn.ts b/src/client/workflow/models/WorkflowIn.ts new file mode 100644 index 0000000000000000000000000000000000000000..544b0e3d50e28167d433cf1dda6a1bfaef7d54f6 --- /dev/null +++ b/src/client/workflow/models/WorkflowIn.ts @@ -0,0 +1,38 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { WorkflowModeIn } from './WorkflowModeIn'; + +export type WorkflowIn = { + /** + * Short descriptive name of the workflow + */ + name: string; + /** + * Short description of the workflow + */ + short_description: string; + /** + * URL to the Git repository belonging to this workflow + */ + repository_url: string; + /** + * Hash of the git commit + */ + git_commit_hash: string; + /** + * Initial version of the Workflow. Should follow semantic versioning + */ + initial_version?: string; + /** + * Token to access the content git repository + */ + token?: (string | null); + /** + * List of modes with alternative entrypoint the new workflow has + */ + modes?: (Array<WorkflowModeIn> | null); +}; + diff --git a/src/client/workflow/models/WorkflowModeIn.ts b/src/client/workflow/models/WorkflowModeIn.ts new file mode 100644 index 0000000000000000000000000000000000000000..a50b038c1abd7c60352b1526e164c652aa99e7df --- /dev/null +++ b/src/client/workflow/models/WorkflowModeIn.ts @@ -0,0 +1,20 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type WorkflowModeIn = { + /** + * Path to the alternative parameter schema + */ + schema_path: string; + /** + * Name of the process the workflow should start with. Argument to the parameter '-entry' + */ + entrypoint: string; + /** + * Name of the workflow mode + */ + name: string; +}; + diff --git a/src/client/workflow/models/WorkflowModeOut.ts b/src/client/workflow/models/WorkflowModeOut.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d52e08bd2580b58af3b5204a6e6ebc1fa2d6ee8 --- /dev/null +++ b/src/client/workflow/models/WorkflowModeOut.ts @@ -0,0 +1,24 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type WorkflowModeOut = { + /** + * Path to the alternative parameter schema + */ + schema_path: string; + /** + * Name of the process the workflow should start with. Argument to the parameter '-entry' + */ + entrypoint: string; + /** + * Name of the workflow mode + */ + name: string; + /** + * ID of the workflow mode + */ + mode_id: string; +}; + diff --git a/src/client/workflow/models/WorkflowOut.ts b/src/client/workflow/models/WorkflowOut.ts index b16961cff5439bd2817580383ca376f5f772af58..a9c6a081189c37ba74c4e5a8db0307fd29d2e07a 100644 --- a/src/client/workflow/models/WorkflowOut.ts +++ b/src/client/workflow/models/WorkflowOut.ts @@ -1,8 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { WorkflowVersionReduced } from './WorkflowVersionReduced'; +import type { WorkflowVersion } from './WorkflowVersion'; export type WorkflowOut = { /** @@ -24,10 +25,14 @@ export type WorkflowOut = { /** * Versions of the workflow */ - versions: Array<WorkflowVersionReduced>; + versions: Array<WorkflowVersion>; /** * Id of developer of the workflow */ developer_id: string; + /** + * Flag if the workflow is hosted in a private git repository + */ + private: boolean; }; diff --git a/src/client/workflow/models/WorkflowStatistic.ts b/src/client/workflow/models/WorkflowStatistic.ts index e9211ec655b249f38790bed6eadf850e6465b7dc..4b7fbade791196f814a52cee40e8cca117ea8102 100644 --- a/src/client/workflow/models/WorkflowStatistic.ts +++ b/src/client/workflow/models/WorkflowStatistic.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/models/WorkflowUpdate.ts b/src/client/workflow/models/WorkflowUpdate.ts new file mode 100644 index 0000000000000000000000000000000000000000..97c2577769795b6fc537326f61b7772fc98ab668 --- /dev/null +++ b/src/client/workflow/models/WorkflowUpdate.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { WorkflowModeIn } from './WorkflowModeIn'; + +export type WorkflowUpdate = { + /** + * Version of the Workflow. Should follow semantic versioning + */ + version: string; + /** + * Hash of the git commit + */ + git_commit_hash: string; + /** + * Add modes to the new workflow version + */ + append_modes?: (Array<WorkflowModeIn> | null); + /** + * Delete modes for the new workflow version. + */ + delete_modes?: (Array<string> | null); +}; + diff --git a/src/client/workflow/models/WorkflowVersionReduced.ts b/src/client/workflow/models/WorkflowVersion.ts similarity index 53% rename from src/client/workflow/models/WorkflowVersionReduced.ts rename to src/client/workflow/models/WorkflowVersion.ts index 17fe045796e7f614c3bf96d67a4fcff004bf4b4e..563cdc6e4db57e903736c8896b28ca734511f12d 100644 --- a/src/client/workflow/models/WorkflowVersionReduced.ts +++ b/src/client/workflow/models/WorkflowVersion.ts @@ -1,14 +1,19 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ import type { Status } from './Status'; -export type WorkflowVersionReduced = { +export type WorkflowVersion = { /** * Status of the workflow version */ status: Status; + /** + * ID of the corresponding workflow + */ + workflow_id: string; /** * Version of the Workflow. Should follow semantic versioning */ @@ -20,10 +25,14 @@ export type WorkflowVersionReduced = { /** * URL of the icon for this workflow version */ - icon_url?: string; + icon_url: (string | null); + /** + * Timestamp when the version was created as UNIX timestamp + */ + created_at: number; /** - * Timestamp when the version was created + * Optional modes his workflow version has */ - created_at: string; + modes: (Array<string> | null); }; diff --git a/src/client/workflow/models/WorkflowVersionFull.ts b/src/client/workflow/models/WorkflowVersionFull.ts deleted file mode 100644 index 1cbe2d9831997f77f3ef5b22903d72fa58586968..0000000000000000000000000000000000000000 --- a/src/client/workflow/models/WorkflowVersionFull.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Status } from './Status'; - -export type WorkflowVersionFull = { - /** - * Status of the workflow version - */ - status: Status; - /** - * Version of the Workflow. Should follow semantic versioning - */ - version: string; - /** - * Hash of the git commit - */ - git_commit_hash: string; - /** - * URL of the icon for this workflow version - */ - icon_url?: string; - /** - * Timestamp when the version was created - */ - created_at: string; - /** - * ID of the corresponding workflow - */ - workflow_id: string; - /** - * URL to download README.md from - */ - readme_url: string; - /** - * URL to download CHANGELOG.md from - */ - changelog_url: string; - /** - * URL to download usage.md from - */ - usage_url: string; - /** - * URL to download output.md from - */ - output_url: string; - /** - * URL to download nextflow_schema.json from - */ - parameter_schema_url: string; -}; - diff --git a/src/client/workflow/models/WorkflowVersionStatus.ts b/src/client/workflow/models/WorkflowVersionStatus.ts index 70c66d12d5ffa7a982266597d9e8e772403e0e21..0c66d1dd6b28229891c5c7d5ceed3eefab291c4e 100644 --- a/src/client/workflow/models/WorkflowVersionStatus.ts +++ b/src/client/workflow/models/WorkflowVersionStatus.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ diff --git a/src/client/workflow/services/WorkflowCredentialsService.ts b/src/client/workflow/services/WorkflowCredentialsService.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f9f891efb06fa6cd1b4304e817b59ef954d6cd7 --- /dev/null +++ b/src/client/workflow/services/WorkflowCredentialsService.ts @@ -0,0 +1,99 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { WorkflowCredentialsIn } from '../models/WorkflowCredentialsIn'; +import type { WorkflowCredentialsOut } from '../models/WorkflowCredentialsOut'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; + +export class WorkflowCredentialsService { + + /** + * Get the credentials of a workflow + * Get the credentials for the repository of a workflow. Only the developer of a workflow can do this. + * + * Permission "workflow:update" required. + * @param wid ID of a workflow + * @returns WorkflowCredentialsOut Successful Response + * @throws ApiError + */ + public static workflowCredentialsGetWorkflowCredentials( + wid: string, + ): CancelablePromise<WorkflowCredentialsOut> { + return __request(OpenAPI, { + method: 'GET', + url: '/workflows/{wid}/credentials', + path: { + 'wid': wid, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Update the credentials of a workflow + * Update the credentials for the repository of a workflow. + * + * Permission "workflow:update" required. + * @param wid ID of a workflow + * @param requestBody + * @returns any Successful Response + * @throws ApiError + */ + public static workflowCredentialsUpdateWorkflowCredentials( + wid: string, + requestBody: WorkflowCredentialsIn, + ): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'PUT', + url: '/workflows/{wid}/credentials', + path: { + 'wid': wid, + }, + body: requestBody, + mediaType: 'application/json', + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Delete the credentials of a workflow + * Delete the credentials for the repository of a workflow. + * + * Permission "workflow:delete" required. + * @param wid ID of a workflow + * @returns void + * @throws ApiError + */ + public static workflowCredentialsDeleteWorkflowCredentials( + wid: string, + ): CancelablePromise<void> { + return __request(OpenAPI, { + method: 'DELETE', + url: '/workflows/{wid}/credentials', + path: { + 'wid': wid, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + +} diff --git a/src/client/workflow/services/WorkflowExecutionService.ts b/src/client/workflow/services/WorkflowExecutionService.ts index bf28bdbd3134a94f11c2f4ede505b45be09f3674..199ba7b7ac8000a30397d7136a0cc7bec8056c1d 100644 --- a/src/client/workflow/services/WorkflowExecutionService.ts +++ b/src/client/workflow/services/WorkflowExecutionService.ts @@ -1,3 +1,4 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ @@ -13,30 +14,23 @@ import { request as __request } from '../core/request'; export class WorkflowExecutionService { /** - * Get all workflow executions - * Get all workflow executions. + * Start a new workflow execution + * Start a new workflow execution. Workflow versions wit status `DEPRECATED` or `DENIED` can't be started. * - * Permission "workflow_execution:list" required, if 'user_id' is the same as the current user, - * otherwise "workflow_execution:list_all" required. - * @param userId Filter for workflow executions by a user. If none, Permission 'workflow_execution:read_any' required. - * @param executionStatus Filter for status of workflow execution - * @param workflowVersionId Filter for workflow version + * Permission "workflow_execution:start" required if workflow versions status is `PUBLISHED`, + * otherwise "workflow_execution:start_unpublished" required. + * @param requestBody * @returns WorkflowExecutionOut Successful Response * @throws ApiError */ - public static workflowExecutionListWorkflowExecutions( - userId?: string, - executionStatus?: Array<WorkflowExecutionStatus>, - workflowVersionId?: string, - ): CancelablePromise<Array<WorkflowExecutionOut>> { + public static workflowExecutionStartWorkflow( + requestBody: WorkflowExecutionIn, + ): CancelablePromise<WorkflowExecutionOut> { return __request(OpenAPI, { - method: 'GET', + method: 'POST', url: '/workflow_executions', - query: { - 'user_id': userId, - 'execution_status': executionStatus, - 'workflow_version_id': workflowVersionId, - }, + body: requestBody, + mediaType: 'application/json', errors: { 400: `Error decoding JWT Token`, 403: `Not authenticated`, @@ -47,23 +41,30 @@ export class WorkflowExecutionService { } /** - * Start a new workflow execution - * Start a new workflow execution. Workflow versions wit status `DEPRECATED` or `DENIED` can't be started. + * Get all workflow executions + * Get all workflow executions. * - * Permission "workflow_execution:start" required if workflow versions status is `PUBLISHED`, - * otherwise "workflow_execution:start_unpublished" required. - * @param requestBody + * Permission "workflow_execution:list" required, if 'user_id' is the same as the current user, + * otherwise "workflow_execution:list_all" required. + * @param userId Filter for workflow executions by a user. If none, Permission 'workflow_execution:read_any' required. + * @param executionStatus Filter for status of workflow execution + * @param workflowVersionId Filter for workflow version * @returns WorkflowExecutionOut Successful Response * @throws ApiError */ - public static workflowExecutionStartWorkflow( - requestBody: WorkflowExecutionIn, - ): CancelablePromise<WorkflowExecutionOut> { + public static workflowExecutionListWorkflowExecutions( + userId?: (string | null), + executionStatus?: (Array<WorkflowExecutionStatus> | null), + workflowVersionId?: (string | null), + ): CancelablePromise<Array<WorkflowExecutionOut>> { return __request(OpenAPI, { - method: 'POST', + method: 'GET', url: '/workflow_executions', - body: requestBody, - mediaType: 'application/json', + query: { + 'user_id': userId, + 'execution_status': executionStatus, + 'workflow_version_id': workflowVersionId, + }, errors: { 400: `Error decoding JWT Token`, 403: `Not authenticated`, @@ -78,6 +79,10 @@ export class WorkflowExecutionService { * Start a new workflow execution from an arbitrary git repository. * * Permission "workflow:create" required. + * + * For private Gitlab repositories, a Project Access Token with the role Reporter and scope `read_api` is needed. + * + * For private GitHub repositories, a Personal Access Token (classic) with scope `repo` is needed. * @param requestBody * @returns WorkflowExecutionOut Successful Response * @throws ApiError @@ -155,6 +160,34 @@ export class WorkflowExecutionService { }); } + /** + * Get the parameters of a workflow execution + * Get the parameters of a specific workflow execution. + * + * Permission "workflow_execution:read" required if the current user started the workflow execution, + * otherwise "workflow_execution:read_any" required. + * @param eid ID of a workflow execution. + * @returns any Successful Response + * @throws ApiError + */ + public static workflowExecutionGetWorkflowExecutionParams( + eid: string, + ): CancelablePromise<Record<string, any>> { + return __request(OpenAPI, { + method: 'GET', + url: '/workflow_executions/{eid}/params', + path: { + 'eid': eid, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + /** * Cancel a workflow execution * Cancel a running workflow execution. diff --git a/src/client/workflow/services/WorkflowModeService.ts b/src/client/workflow/services/WorkflowModeService.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c9757918efd364406294002c88e7a1eecc2c1ad --- /dev/null +++ b/src/client/workflow/services/WorkflowModeService.ts @@ -0,0 +1,40 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { WorkflowModeOut } from '../models/WorkflowModeOut'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; + +export class WorkflowModeService { + + /** + * List workflows + * Get a workflow mode + * + * Permission 'workflow:read' required + * @param modeId ID of a workflow mode + * @returns WorkflowModeOut Successful Response + * @throws ApiError + */ + public static workflowModeGetWorkflowMode( + modeId: string, + ): CancelablePromise<WorkflowModeOut> { + return __request(OpenAPI, { + method: 'GET', + url: '/workflow_modes/{mode_id}', + path: { + 'mode_id': modeId, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + +} diff --git a/src/client/workflow/services/WorkflowService.ts b/src/client/workflow/services/WorkflowService.ts index 45f9ffa369494568f828b205f6c156855d2096e4..dad2fcb71c8296bc1ca20c2989db666d735a599a 100644 --- a/src/client/workflow/services/WorkflowService.ts +++ b/src/client/workflow/services/WorkflowService.ts @@ -1,12 +1,16 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Body_Workflow_create_workflow } from '../models/Body_Workflow_create_workflow'; -import type { Body_Workflow_update_workflow } from '../models/Body_Workflow_update_workflow'; +import type { Body_Workflow_Version_upload_workflow_version_icon } from '../models/Body_Workflow_Version_upload_workflow_version_icon'; +import type { DocumentationEnum } from '../models/DocumentationEnum'; +import type { IconUpdateOut } from '../models/IconUpdateOut'; import type { Status } from '../models/Status'; +import type { WorkflowIn } from '../models/WorkflowIn'; import type { WorkflowOut } from '../models/WorkflowOut'; import type { WorkflowStatistic } from '../models/WorkflowStatistic'; -import type { WorkflowVersionFull } from '../models/WorkflowVersionFull'; +import type { WorkflowUpdate } from '../models/WorkflowUpdate'; +import type { WorkflowVersion } from '../models/WorkflowVersion'; import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus'; import type { CancelablePromise } from '../core/CancelablePromise'; @@ -27,9 +31,9 @@ export class WorkflowService { * @throws ApiError */ public static workflowListWorkflows( - nameSubstring?: string, - versionStatus?: Array<Status>, - developerId?: string, + nameSubstring?: (string | null), + versionStatus?: (Array<Status> | null), + developerId?: (string | null), ): CancelablePromise<Array<WorkflowOut>> { return __request(OpenAPI, { method: 'GET', @@ -51,20 +55,24 @@ export class WorkflowService { /** * Create a new workflow * Create a new workflow. - * R + * * Permission "workflow:create" required. - * @param formData + * + * For private Gitlab repositories, a Project Access Token with the role Reporter and scope `read_api` is needed. + * + * For private GitHub repositories, a Personal Access Token (classic) with scope `repo` is needed. + * @param requestBody * @returns WorkflowOut Successful Response * @throws ApiError */ public static workflowCreateWorkflow( - formData: Body_Workflow_create_workflow, + requestBody: WorkflowIn, ): CancelablePromise<WorkflowOut> { return __request(OpenAPI, { method: 'POST', url: '/workflows', - formData: formData, - mediaType: 'multipart/form-data', + body: requestBody, + mediaType: 'application/json', errors: { 400: `Error decoding JWT Token`, 403: `Not authenticated`, @@ -86,7 +94,7 @@ export class WorkflowService { */ public static workflowGetWorkflow( wid: string, - versionStatus?: Array<Status>, + versionStatus?: (Array<Status> | null), ): CancelablePromise<WorkflowOut> { return __request(OpenAPI, { method: 'GET', @@ -164,22 +172,22 @@ export class WorkflowService { * * Permission "workflow:update" required. * @param wid ID of a workflow - * @param formData - * @returns WorkflowVersionFull Successful Response + * @param requestBody + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowUpdateWorkflow( wid: string, - formData: Body_Workflow_update_workflow, - ): CancelablePromise<WorkflowVersionFull> { + requestBody: WorkflowUpdate, + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'POST', url: '/workflows/{wid}/update', path: { 'wid': wid, }, - formData: formData, - mediaType: 'multipart/form-data', + body: requestBody, + mediaType: 'application/json', errors: { 400: `Error decoding JWT Token`, 403: `Not authenticated`, @@ -196,13 +204,13 @@ export class WorkflowService { * Permission "workflow:list" required. * @param wid ID of a workflow * @param versionStatus Which versions of the workflow to include in the response. Permission 'workflow:list_filter' required if you are not the developer of this workflow. Default PUBLISHED and DEPRECATED - * @returns WorkflowVersionFull Successful Response + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionListWorkflowVersion( wid: string, - versionStatus?: Array<Status>, - ): CancelablePromise<Array<WorkflowVersionFull>> { + versionStatus?: (Array<Status> | null), + ): CancelablePromise<Array<WorkflowVersion>> { return __request(OpenAPI, { method: 'GET', url: '/workflows/{wid}/versions', @@ -229,13 +237,13 @@ export class WorkflowService { * otherwise "workflow:read_any" * @param gitCommitHash Git commit git_commit_hash of specific version or 'latest'. * @param wid ID of a workflow - * @returns WorkflowVersionFull Successful Response + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionGetWorkflowVersion( gitCommitHash: string, wid: string, - ): CancelablePromise<WorkflowVersionFull> { + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'GET', url: '/workflows/{wid}/versions/{git_commit_hash}', @@ -260,14 +268,14 @@ export class WorkflowService { * @param gitCommitHash Git commit git_commit_hash of specific version. * @param wid ID of a workflow * @param requestBody - * @returns WorkflowVersionFull Successful Response + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionUpdateWorkflowVersionStatus( gitCommitHash: string, wid: string, requestBody: WorkflowVersionStatus, - ): CancelablePromise<WorkflowVersionFull> { + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'PATCH', url: '/workflows/{wid}/versions/{git_commit_hash}/status', @@ -291,22 +299,125 @@ export class WorkflowService { * Deprecate a workflow version. * * Permission "workflow:update" required if you are the developer of the workflow, - * otherwise "workflow:read_status"" - * @param gitCommitHash Git commit git_commit_hash of specific version. + * otherwise "workflow:read_status" * @param wid ID of a workflow - * @returns WorkflowVersionFull Successful Response + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionDeprecateWorkflowVersion( - gitCommitHash: string, wid: string, - ): CancelablePromise<WorkflowVersionFull> { + gitCommitHash: string, + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'POST', url: '/workflows/{wid}/versions/{git_commit_hash}/deprecate', path: { + 'wid': wid, 'git_commit_hash': gitCommitHash, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Fetch documentation for a workflow version + * Get the documentation for a specific workflow version. + * Streams the response directly from the right git repository. + * + * Permission "workflow:read" required. + * @param wid ID of a workflow + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @param document Specific which type of documentation the client wants to fetch + * @param modeId Workflow Mode + * @returns any Successful Response + * @throws ApiError + */ + public static workflowVersionDownloadWorkflowDocumentation( + wid: string, + gitCommitHash: string, + document?: DocumentationEnum, + modeId?: (string | null), + ): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/workflows/{wid}/versions/{git_commit_hash}/documentation', + path: { 'wid': wid, + 'git_commit_hash': gitCommitHash, + }, + query: { + 'document': document, + 'mode_id': modeId, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Upload icon for workflow version + * Upload an icon for the workflow version and returns the new icon URL. + * + * Permission "workflow:update" required. + * @param wid ID of a workflow + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @param formData + * @returns IconUpdateOut Successful Response + * @throws ApiError + */ + public static workflowVersionUploadWorkflowVersionIcon( + wid: string, + gitCommitHash: string, + formData: Body_Workflow_Version_upload_workflow_version_icon, + ): CancelablePromise<IconUpdateOut> { + return __request(OpenAPI, { + method: 'POST', + url: '/workflows/{wid}/versions/{git_commit_hash}/icon', + path: { + 'wid': wid, + 'git_commit_hash': gitCommitHash, + }, + formData: formData, + mediaType: 'multipart/form-data', + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Delete icon of workflow version + * Delete the icon of the workflow version. + * + * Permission "workflow:update" required. + * @param wid ID of a workflow + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @returns void + * @throws ApiError + */ + public static workflowVersionDeleteWorkflowVersionIcon( + wid: string, + gitCommitHash: string, + ): CancelablePromise<void> { + return __request(OpenAPI, { + method: 'DELETE', + url: '/workflows/{wid}/versions/{git_commit_hash}/icon', + path: { + 'wid': wid, + 'git_commit_hash': gitCommitHash, }, errors: { 400: `Error decoding JWT Token`, diff --git a/src/client/workflow/services/WorkflowVersionService.ts b/src/client/workflow/services/WorkflowVersionService.ts index 25687b7b5fc92b1cb50a952ac391398461950a42..0d46dd904a575a8b3b60d2d399b494f6dce0182a 100644 --- a/src/client/workflow/services/WorkflowVersionService.ts +++ b/src/client/workflow/services/WorkflowVersionService.ts @@ -1,8 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Body_Workflow_Version_upload_workflow_version_icon } from '../models/Body_Workflow_Version_upload_workflow_version_icon'; +import type { DocumentationEnum } from '../models/DocumentationEnum'; +import type { IconUpdateOut } from '../models/IconUpdateOut'; import type { Status } from '../models/Status'; -import type { WorkflowVersionFull } from '../models/WorkflowVersionFull'; +import type { WorkflowVersion } from '../models/WorkflowVersion'; import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus'; import type { CancelablePromise } from '../core/CancelablePromise'; @@ -18,13 +22,13 @@ export class WorkflowVersionService { * Permission "workflow:list" required. * @param wid ID of a workflow * @param versionStatus Which versions of the workflow to include in the response. Permission 'workflow:list_filter' required if you are not the developer of this workflow. Default PUBLISHED and DEPRECATED - * @returns WorkflowVersionFull Successful Response + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionListWorkflowVersion( wid: string, - versionStatus?: Array<Status>, - ): CancelablePromise<Array<WorkflowVersionFull>> { + versionStatus?: (Array<Status> | null), + ): CancelablePromise<Array<WorkflowVersion>> { return __request(OpenAPI, { method: 'GET', url: '/workflows/{wid}/versions', @@ -51,13 +55,13 @@ export class WorkflowVersionService { * otherwise "workflow:read_any" * @param gitCommitHash Git commit git_commit_hash of specific version or 'latest'. * @param wid ID of a workflow - * @returns WorkflowVersionFull Successful Response + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionGetWorkflowVersion( gitCommitHash: string, wid: string, - ): CancelablePromise<WorkflowVersionFull> { + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'GET', url: '/workflows/{wid}/versions/{git_commit_hash}', @@ -82,14 +86,14 @@ export class WorkflowVersionService { * @param gitCommitHash Git commit git_commit_hash of specific version. * @param wid ID of a workflow * @param requestBody - * @returns WorkflowVersionFull Successful Response + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionUpdateWorkflowVersionStatus( gitCommitHash: string, wid: string, requestBody: WorkflowVersionStatus, - ): CancelablePromise<WorkflowVersionFull> { + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'PATCH', url: '/workflows/{wid}/versions/{git_commit_hash}/status', @@ -113,22 +117,125 @@ export class WorkflowVersionService { * Deprecate a workflow version. * * Permission "workflow:update" required if you are the developer of the workflow, - * otherwise "workflow:read_status"" - * @param gitCommitHash Git commit git_commit_hash of specific version. + * otherwise "workflow:read_status" * @param wid ID of a workflow - * @returns WorkflowVersionFull Successful Response + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @returns WorkflowVersion Successful Response * @throws ApiError */ public static workflowVersionDeprecateWorkflowVersion( - gitCommitHash: string, wid: string, - ): CancelablePromise<WorkflowVersionFull> { + gitCommitHash: string, + ): CancelablePromise<WorkflowVersion> { return __request(OpenAPI, { method: 'POST', url: '/workflows/{wid}/versions/{git_commit_hash}/deprecate', path: { + 'wid': wid, + 'git_commit_hash': gitCommitHash, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Fetch documentation for a workflow version + * Get the documentation for a specific workflow version. + * Streams the response directly from the right git repository. + * + * Permission "workflow:read" required. + * @param wid ID of a workflow + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @param document Specific which type of documentation the client wants to fetch + * @param modeId Workflow Mode + * @returns any Successful Response + * @throws ApiError + */ + public static workflowVersionDownloadWorkflowDocumentation( + wid: string, + gitCommitHash: string, + document?: DocumentationEnum, + modeId?: (string | null), + ): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/workflows/{wid}/versions/{git_commit_hash}/documentation', + path: { + 'wid': wid, + 'git_commit_hash': gitCommitHash, + }, + query: { + 'document': document, + 'mode_id': modeId, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Upload icon for workflow version + * Upload an icon for the workflow version and returns the new icon URL. + * + * Permission "workflow:update" required. + * @param wid ID of a workflow + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @param formData + * @returns IconUpdateOut Successful Response + * @throws ApiError + */ + public static workflowVersionUploadWorkflowVersionIcon( + wid: string, + gitCommitHash: string, + formData: Body_Workflow_Version_upload_workflow_version_icon, + ): CancelablePromise<IconUpdateOut> { + return __request(OpenAPI, { + method: 'POST', + url: '/workflows/{wid}/versions/{git_commit_hash}/icon', + path: { + 'wid': wid, 'git_commit_hash': gitCommitHash, + }, + formData: formData, + mediaType: 'multipart/form-data', + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + + /** + * Delete icon of workflow version + * Delete the icon of the workflow version. + * + * Permission "workflow:update" required. + * @param wid ID of a workflow + * @param gitCommitHash Git commit git_commit_hash of specific version. + * @returns void + * @throws ApiError + */ + public static workflowVersionDeleteWorkflowVersionIcon( + wid: string, + gitCommitHash: string, + ): CancelablePromise<void> { + return __request(OpenAPI, { + method: 'DELETE', + url: '/workflows/{wid}/versions/{git_commit_hash}/icon', + path: { 'wid': wid, + 'git_commit_hash': gitCommitHash, }, errors: { 400: `Error decoding JWT Token`, diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue index 192d93f679bd7146de30821b9deea9fcdeaf425c..2c08939a0e2769614f48e6c99a829fa8469d8094 100644 --- a/src/components/NavbarTop.vue +++ b/src/components/NavbarTop.vue @@ -21,10 +21,10 @@ function logout() { const activeRoute = ref(""); const objectStorageActive = computed<boolean>( - () => activeRoute.value == "buckets" || activeRoute.value == "s3_keys" + () => activeRoute.value == "buckets" || activeRoute.value == "s3_keys", ); const workflowActive = computed<boolean>( - () => activeRoute.value == "workflows" + () => activeRoute.value == "workflows", ); watch( @@ -41,7 +41,7 @@ watch( } else { activeRoute.value = ""; } - } + }, ); </script> diff --git a/src/components/modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue index e7ce11b160cf6ef6c5ba935f07b41b7e63d957ac..990cfd8256b31a3ec5f48b0ff131fe1f7be769fd 100644 --- a/src/components/modals/SearchUserModal.vue +++ b/src/components/modals/SearchUserModal.vue @@ -41,7 +41,7 @@ watch( formState.loading = false; formState.potentialUsers = []; } - } + }, ); const emit = defineEmits<{ @@ -58,7 +58,7 @@ function searchUser(name: string) { UserService.userListUsers(name) .then((userSuggestions) => { formState.potentialUsers = userSuggestions.filter( - (user) => store.currentUID != user.uid + (user) => store.currentUID != user.uid, ); }) .catch((err) => { diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue index 889466b74a81c1e4c0149852597ee33f8572b853..bd333f7345769d89b4b88b0d53bd02825589922c 100644 --- a/src/components/object-storage/BucketListItem.vue +++ b/src/components/object-storage/BucketListItem.vue @@ -26,14 +26,9 @@ const permissionRepository = useBucketStore(); const router = useRouter(); const permission = computed<BucketPermissionOut | undefined>(() => - permissionRepository.getBucketPermission(props.bucket.name) + permissionRepository.getBucketPermission(props.bucket.name), ); -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.bucket.name.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); - const emit = defineEmits<{ (e: "delete-bucket", bucketName: string): void; }>(); @@ -91,9 +86,7 @@ onMounted(() => { params: { bucketName: bucket.name, subFolders: [] }, }" > - <span class="text-truncate" style="width: 80%">{{ - bucketNameWithoutTenant - }}</span> + <span class="text-truncate" style="width: 80%">{{ bucket.name }}</span> <div> <font-awesome-icon v-if="props.active && !permission && props.deletable" @@ -135,10 +128,10 @@ onMounted(() => { :id="'tooltip-' + randomIDSuffix" data-bs-toggle="tooltip" :data-bs-title=" - dayjs(bucket.created_at).format('DD.MM.YYYY HH:mm:ss') + dayjs.unix(bucket.created_at).format('DD.MM.YYYY HH:mm:ss') " > - {{ dayjs(bucket.created_at).fromNow() }} + {{ dayjs.unix(bucket.created_at).fromNow() }} </span> </td> </tr> @@ -148,7 +141,9 @@ onMounted(() => { </tr> <tr> <th scope="row" class="fw-bold">Size:</th> - <td>{{ filesize(bucket.size) }}</td> + <td> + {{ filesize(bucket.size, { base: 2, standard: "jedec" }) }} + </td> </tr> </tbody> </table> diff --git a/src/components/object-storage/modals/BucketDetailModal.vue b/src/components/object-storage/modals/BucketDetailModal.vue index 5fbff6fdd8269d2f5e8b655cba4afc60e093812c..3ee95a711befbbc7541e4764989ff6580e8a5023 100644 --- a/src/components/object-storage/modals/BucketDetailModal.vue +++ b/src/components/object-storage/modals/BucketDetailModal.vue @@ -3,17 +3,11 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import type { BucketOut } from "@/client/s3proxy"; import dayjs from "dayjs"; import { filesize } from "filesize"; -import { computed } from "vue"; const props = defineProps<{ modalID: string; bucket: BucketOut; }>(); - -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.bucket.name.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); </script> <template> @@ -34,13 +28,15 @@ const bucketNameWithoutTenant = computed<string>(() => { <tbody> <tr> <th scope="row" class="col-2">Name</th> - <td class="col-10">{{ bucketNameWithoutTenant }}</td> + <td class="col-10">{{ props.bucket.name }}</td> </tr> <tr> <th scope="row">Creation date</th> <td> {{ - dayjs(props.bucket.created_at).format("YYYY-MM-DD HH:mm:ss") + dayjs + .unix(props.bucket.created_at) + .format("YYYY-MM-DD HH:mm:ss") }} </td> </tr> @@ -50,7 +46,11 @@ const bucketNameWithoutTenant = computed<string>(() => { </tr> <tr> <th scope="row">Size</th> - <td>{{ filesize(props.bucket.size) }}</td> + <td> + {{ + filesize(props.bucket.size, { base: 2, standard: "jedec" }) + }} + </td> </tr> <tr> <th scope="row">Description</th> diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue index 010da42b3633b5e0df21136365e73e5c14455865..32313142e8a8b9acfaadebd1353f0b67fa12c124 100644 --- a/src/components/object-storage/modals/CopyObjectModal.vue +++ b/src/components/object-storage/modals/CopyObjectModal.vue @@ -39,16 +39,11 @@ function getFileName(key: string): string { return splittedKey[splittedKey.length - 1]; } -function bucketNameWithoutTenant(name: string): string { - const splittedBucketName = name.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -} - function copyObject() { const command = new CopyObjectCommand({ Bucket: formState.destBucket, CopySource: encodeURI( - `/${props.sourceObject.bucket}/${props.sourceObject.key}` + `/${props.sourceObject.bucket}/${props.sourceObject.key}`, ), Key: formState.destKey, }); @@ -84,7 +79,7 @@ watch( () => props.sourceObject.key, (newKey) => { formState.destKey = newKey; - } + }, ); onMounted(() => { @@ -174,7 +169,7 @@ onMounted(() => { :key="bucket.name" :value="bucket.name" > - {{ bucketNameWithoutTenant(bucket.name) }} + {{ bucket.name }} </option> </select> </div> diff --git a/src/components/object-storage/modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue index 69d625863ec8dc4fc61d7bca107bd85c08930dd4..94fe7288e46131437e33d981575aa6696f423f6e 100644 --- a/src/components/object-storage/modals/CreateBucketModal.vue +++ b/src/components/object-storage/modals/CreateBucketModal.vue @@ -61,7 +61,7 @@ function createBucket() { }, () => { formState.loading = false; - } + }, ); } } diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue index 8484696e7486de6fc177606d879256684fe300d8..627b3a8428c672e99c2cffeb8c9e25c7bfd51203 100644 --- a/src/components/object-storage/modals/CreateFolderModal.vue +++ b/src/components/object-storage/modals/CreateFolderModal.vue @@ -25,11 +25,6 @@ const emit = defineEmits<{ (e: "folder-created", object: S3ObjectMetaInformation): void; }>(); -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.bucketName.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); - const formState = reactive<{ folderName: string; uploading: boolean; @@ -47,13 +42,11 @@ function uploadFolder() { .replace(/(\/)\1+/g, "/") .split("") .reverse(); - console.log(reversedKey); const firstLetterIndex = reversedKey.findIndex((char) => char !== "/"); if (firstLetterIndex < 0) { return; } const realKey = reversedKey.slice(firstLetterIndex).reverse().join("") + "/"; - console.log(realKey); const command = new PutObjectCommand({ Bucket: props.bucketName, Body: "", @@ -143,7 +136,7 @@ onMounted(() => { <template v-slot:header> <h4>Create folder in</h4> <ol class="breadcrumb"> - <li class="breadcrumb-item">{{ bucketNameWithoutTenant }}</li> + <li class="breadcrumb-item">{{ props.bucketName }}</li> <li class="breadcrumb-item" v-for="folder in currentFolders" diff --git a/src/components/object-storage/modals/ObjectDetailModal.vue b/src/components/object-storage/modals/ObjectDetailModal.vue index 6d929efd8f90a179c2bc7da79f990320cd6f4365..cea5b4adddf382235a706683f8bb8522770fe934 100644 --- a/src/components/object-storage/modals/ObjectDetailModal.vue +++ b/src/components/object-storage/modals/ObjectDetailModal.vue @@ -3,17 +3,11 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import type { S3ObjectMetaInformation } from "@/client/s3proxy"; import dayjs from "dayjs"; import { filesize } from "filesize"; -import { computed } from "vue"; const props = defineProps<{ modalID: string; s3Object: S3ObjectMetaInformation; }>(); - -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.s3Object.bucket.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); </script> <template> @@ -31,7 +25,7 @@ const bucketNameWithoutTenant = computed<string>(() => { <tbody> <tr> <th scope="row" class="col-4">Bucket</th> - <td class="col-8">{{ bucketNameWithoutTenant }}</td> + <td class="col-8">{{ props.s3Object.bucket }}</td> </tr> <tr> <th scope="row">Name</th> @@ -46,14 +40,18 @@ const bucketNameWithoutTenant = computed<string>(() => { <td> {{ dayjs(props.s3Object.last_modified).format( - "YYYY-MM-DD HH:mm:ss" + "YYYY-MM-DD HH:mm:ss", ) }} </td> </tr> <tr> <th scope="row">Size</th> - <td>{{ filesize(props.s3Object.size) }}</td> + <td> + {{ + filesize(props.s3Object.size, { base: 2, standard: "jedec" }) + }} + </td> </tr> </tbody> </table> diff --git a/src/components/object-storage/modals/PermissionListModal.vue b/src/components/object-storage/modals/PermissionListModal.vue index e28671a591b21625c9fa45898c267442b143765e..71ad1ed8937c4165fd30a836b169921f70a359db 100644 --- a/src/components/object-storage/modals/PermissionListModal.vue +++ b/src/components/object-storage/modals/PermissionListModal.vue @@ -30,6 +30,9 @@ const state = reactive<{ uid: "uid", permission: "READ", grantee_display_name: "display_name", + from_timestamp: null, + to_timestamp: null, + file_prefix: null, }, }); @@ -41,7 +44,7 @@ watch( () => props.bucketName, (newBucketName) => { updateBucketPermissions(newBucketName); - } + }, ); watch( @@ -50,7 +53,7 @@ watch( if (newBucketPermission !== undefined) { state.permissions.push(newBucketPermission); } - } + }, ); // Function @@ -71,7 +74,7 @@ function updateBucketPermissions(bucketName: string) { function permissionDeleted(bucketPermission: BucketPermissionIn) { state.permissions = state.permissions.filter( - (perm) => perm.uid != bucketPermission.uid + (perm) => perm.uid != bucketPermission.uid, ); } @@ -81,7 +84,7 @@ function permissionCreated(bucketPermission: BucketPermissionOut) { function permissionEdited(bucketPermission: BucketPermissionOut) { const index = state.permissions.findIndex( - (perm) => perm.uid == bucketPermission.uid + (perm) => perm.uid == bucketPermission.uid, ); state.permissions[index] = bucketPermission; } diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue index b5e4b2353d4787c5960632ff8674839683a60073..c94f63eecd6d907645968c535dfaf8ff0970316b 100644 --- a/src/components/object-storage/modals/PermissionModal.vue +++ b/src/components/object-storage/modals/PermissionModal.vue @@ -66,22 +66,17 @@ const permissionForm = ref<HTMLFormElement | undefined>(undefined); // Computes Properties // ----------------------------------------------------------------------------- const editPermission = computed<boolean>( - () => props.editUserPermission != undefined + () => props.editUserPermission != undefined, ); const possibleSubFolders = computed<string[]>(() => - findSubFolders(props.subFolders, []) + findSubFolders(props.subFolders, []), ); const permissionUserReadonly = computed<boolean>(() => { return formState.readonly || editPermission.value; }); -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.bucketName.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); - // Watchers // ----------------------------------------------------------------------------- watch( @@ -89,12 +84,12 @@ watch( (newBucketName) => { updatePermission(); permission.bucket_name = newBucketName; - } + }, ); watch( () => props.editUserPermission, - () => updatePermission() + () => updatePermission(), ); // Events @@ -129,7 +124,7 @@ function toastHidden() { * Check if an input should be visible based on its state * @param input Input which visibility should be determined. */ -function inputVisible(input?: string): boolean { +function inputVisible(input?: number | string | null): boolean { return !formState.readonly || input != undefined; } @@ -142,14 +137,8 @@ function updatePermission() { permission.file_prefix = props.editUserPermission.file_prefix; permission.uid = props.editUserPermission.uid; formState.grantee_name = props.editUserPermission.grantee_display_name; - permission.from_timestamp = - props.editUserPermission.from_timestamp != null - ? dayjs(props.editUserPermission.from_timestamp).format("YYYY-MM-DD") - : undefined; - permission.to_timestamp = - props.editUserPermission.to_timestamp != null - ? dayjs(props.editUserPermission.to_timestamp).format("YYYY-MM-DD") - : undefined; + permission.from_timestamp = props.editUserPermission.from_timestamp; + permission.to_timestamp = props.editUserPermission.to_timestamp; permission.permission = props.editUserPermission.permission; } else { permission.file_prefix = undefined; @@ -168,7 +157,7 @@ function updatePermission() { */ function findSubFolders( currentFolder: FolderTree, - parentFolders: string[] + parentFolders: string[], ): string[] { const arr: string[] = []; for (const subFolder of Object.keys(currentFolder.subFolders)) { @@ -180,8 +169,8 @@ function findSubFolders( subFolderString, ...findSubFolders( currentFolder.subFolders[subFolder], - subFolderString.slice(0, subFolderString.length - 1).split("/") - ) + subFolderString.slice(0, subFolderString.length - 1).split("/"), + ), ); } return arr; @@ -193,34 +182,19 @@ function findSubFolders( function formSubmit() { formState.error = false; if (permissionForm.value?.checkValidity()) { - const tempPermission: BucketPermissionIn = permission; - if (permission.from_timestamp != null) { - tempPermission.from_timestamp = - permission.from_timestamp.length > 0 - ? dayjs(permission.from_timestamp).toISOString() - : undefined; - } - if (permission.to_timestamp != null) { - tempPermission.to_timestamp = - permission.to_timestamp.length > 0 - ? dayjs(permission.to_timestamp).toISOString() - : undefined; - } formState.loading = true; const serverAnswerPromise = editPermission.value ? BucketPermissionService.bucketPermissionUpdatePermission( permission.uid, permission.bucket_name, { - to_timestamp: tempPermission.to_timestamp, - from_timestamp: tempPermission.from_timestamp, - permission: tempPermission.permission, - file_prefix: tempPermission.file_prefix, - } as BucketPermissionParameters + to_timestamp: permission.to_timestamp, + from_timestamp: permission.from_timestamp, + permission: permission.permission, + file_prefix: permission.file_prefix, + } as BucketPermissionParameters, ) - : BucketPermissionService.bucketPermissionCreatePermission( - tempPermission - ); + : BucketPermissionService.bucketPermissionCreatePermission(permission); serverAnswerPromise .then((permission: BucketPermissionOut) => { if (editPermission.value) { @@ -251,7 +225,7 @@ function confirmedDeletePermission(bucketName: string, uid: string) { formState.loading = true; BucketPermissionService.bucketPermissionDeletePermissionForBucket( bucketName, - uid + uid, ) .then(() => { permissionDeleted.value = true; @@ -272,6 +246,7 @@ function updateUser(user: User) { permission.uid = user.uid; formState.grantee_name = user.display_name; } + // Lifecycle Hooks // ----------------------------------------------------------------------------- onMounted(() => { @@ -279,6 +254,18 @@ onMounted(() => { successToast = new Toast("#" + "toast-" + randomIDSuffix); updatePermission(); }); + +function fromTimestampChanged(target?: HTMLInputElement | null) { + permission.from_timestamp = target?.value + ? dayjs(target?.value).unix() + : undefined; +} + +function toTimestampChanged(target?: HTMLInputElement | null) { + permission.to_timestamp = target?.value + ? dayjs(target?.value).unix() + : undefined; +} </script> <template> @@ -334,7 +321,7 @@ onMounted(() => { <template v-slot:header v-else-if="props.editUserPermission !== undefined" >Edit Permission </template> - <template v-slot:header v-else> Create new Permission </template> + <template v-slot:header v-else> Create new Permission</template> <template v-slot:extra-button> <font-awesome-icon v-if="props.deletable" @@ -368,7 +355,7 @@ onMounted(() => { class="form-control-plaintext" id="bucketNameInput" required - :value="bucketNameWithoutTenant" + :value="permission.bucket_name" /> </div> </div> @@ -437,7 +424,15 @@ onMounted(() => { id="permissionDateFromInput" :readonly="formState.readonly" :min="dayjs().format('YYYY-MM-DD')" - v-model="permission.from_timestamp" + :value=" + permission.from_timestamp + ? dayjs.unix(permission.from_timestamp).format('YYYY-MM-DD') + : undefined + " + @input=" + (event) => + fromTimestampChanged(event.target as HTMLInputElement) + " /> </div> <label @@ -453,14 +448,22 @@ onMounted(() => { class="form-control" id="permissionToFromInput" :readonly="formState.readonly" - v-model="permission.to_timestamp" :min=" permission.from_timestamp != null - ? dayjs(permission.from_timestamp) + ? dayjs + .unix(permission.from_timestamp) .add(1, 'day') .format('YYYY-MM-DD') : dayjs().add(1, 'day').format('YYYY-MM-DD') " + :value=" + permission.to_timestamp + ? dayjs.unix(permission.to_timestamp).format('YYYY-MM-DD') + : undefined + " + @input=" + (event) => toTimestampChanged(event.target as HTMLInputElement) + " /> </div> </div> @@ -553,6 +556,7 @@ onMounted(() => { .pseudo-link { color: var(--bs-secondary); } + .pseudo-link:hover { color: var(--bs-link-hover-color); } @@ -560,6 +564,7 @@ onMounted(() => { .delete-icon { color: var(--bs-secondary) !important; } + .delete-icon:hover { color: var(--bs-danger) !important; } diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue index b123b883266ce42f14875ebad4f0e42a0a6905a0..79a96f6689a614f38db33a389db3ce2246978559 100644 --- a/src/components/object-storage/modals/UploadObjectModal.vue +++ b/src/components/object-storage/modals/UploadObjectModal.vue @@ -5,8 +5,10 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { computed, onMounted, reactive, ref, watch } from "vue"; import type { S3ObjectMetaInformation } from "@/client/s3proxy"; import dayjs from "dayjs"; -import { filesize } from "filesize"; import { Modal, Toast } from "bootstrap"; +import { partial } from "filesize"; + +const fsize = partial({ base: 2, standard: "jedec" }); const props = defineProps<{ modalID: string; @@ -32,7 +34,7 @@ watch( () => props.editObjectFileName, (nextFileName) => { formState.key = nextFileName ?? ""; - } + }, ); const formState = reactive({ @@ -50,18 +52,13 @@ const formState = reactive({ }); const uploadProgress = computed<number>(() => - Math.round((100 * formState.uploadDone) / formState.uploadTotal) + Math.round((100 * formState.uploadDone) / formState.uploadTotal), ); const editObject = computed<boolean>( - () => props.editObjectFileName !== undefined + () => props.editObjectFileName !== undefined, ); -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.bucketName.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); - async function uploadObject() { const key = props.keyPrefix.length > 0 @@ -180,7 +177,7 @@ onMounted(() => { <template v-slot:header> <h4>Upload file to</h4> <ol class="breadcrumb"> - <li class="breadcrumb-item">{{ bucketNameWithoutTenant }}</li> + <li class="breadcrumb-item">{{ props.bucketName }}</li> <li class="breadcrumb-item" v-for="folder in currentFolders" @@ -263,8 +260,8 @@ onMounted(() => { </div> </div> <span v-if="formState.uploadDone > 0"> - {{ filesize(formState.uploadDone) }} / - {{ filesize(formState.uploadTotal) }} + {{ fsize(formState.uploadDone) }} / + {{ fsize(formState.uploadTotal) }} </span> </div> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> diff --git a/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue b/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue index d5b940c916c16a557c3ae41a514600d80b0b9e52..df8d127eafb60b82640405358fbe5a08ffd2429b 100644 --- a/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue +++ b/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue @@ -30,13 +30,13 @@ const navParameterGroups = computed<ParameterGroup[]>(() => (group) => Object.keys(parameterGroups.value[group.group]["properties"]).filter( (key) => - !parameterGroups.value[group.group]["properties"][key]["hidden"] - ).length > 0 - ) + !parameterGroups.value[group.group]["properties"][key]["hidden"], + ).length > 0, + ), ); const parameterGroups = computed<Record<string, never>>( - () => props.schema["definitions"] + () => props.schema["definitions"], ); </script> diff --git a/src/components/parameter-schema/ParameterSchemaFormComponent.vue b/src/components/parameter-schema/ParameterSchemaFormComponent.vue index 096e2ce775528fe9c702bd4fc1bd95416b5c715e..d59dd130a49818b67eef8848ae94754fa04ac6cf 100644 --- a/src/components/parameter-schema/ParameterSchemaFormComponent.vue +++ b/src/components/parameter-schema/ParameterSchemaFormComponent.vue @@ -27,7 +27,7 @@ const emit = defineEmits<{ // eslint-disable-next-line @typescript-eslint/no-explicit-any parameters: Record<string, any>, notes?: string, - report_output_bucket?: string + report_output_bucket?: string, ): void; }>(); @@ -73,7 +73,7 @@ const formState = reactive<{ // Computed Properties // ============================================================================= const parameterGroups = computed<Record<string, never>>( - () => props.schema?.["definitions"] + () => props.schema?.["definitions"], ); // Create a list with the names of all parameter groups @@ -91,9 +91,9 @@ const navParameterGroups = computed<ParameterGroup[]>(() => (group) => Object.keys(parameterGroups.value[group.group]["properties"]).filter( (key) => - !parameterGroups.value[group.group]["properties"][key]["hidden"] - ).length > 0 - ) + !parameterGroups.value[group.group]["properties"][key]["hidden"], + ).length > 0, + ), ); // Watchers @@ -104,7 +104,7 @@ watch( if (newValue) { updateSchema(newValue); } - } + }, ); // Functions @@ -122,7 +122,7 @@ function updateSchema(schema: Record<string, any>) { parameterName, // @ts-ignore parameter["default"], - ]) + ]), ), ]); formState.formInput = Object.fromEntries(b); @@ -134,7 +134,7 @@ function startWorkflow() { formState.errorType = undefined; if (launchForm.value?.checkValidity()) { const realInput: Record<string, any> = Object.values( - formState.formInput + formState.formInput, ).reduce((acc, val) => { return { ...acc, ...val }; }); @@ -148,7 +148,7 @@ function startWorkflow() { "start-workflow", realInput, formState.pipelineNotes, - formState.report_bucket + formState.report_bucket, ); } } else { diff --git a/src/components/parameter-schema/description-mode/ParameterDescription.vue b/src/components/parameter-schema/description-mode/ParameterDescription.vue index 4e5f518589d1358343b7a1b54cf8d21cddd1239b..cd7a63ef1c7ac3b2acfb8f780220663e6c3ebc00 100644 --- a/src/components/parameter-schema/description-mode/ParameterDescription.vue +++ b/src/components/parameter-schema/description-mode/ParameterDescription.vue @@ -21,27 +21,27 @@ const props = defineProps({ const randomIDSuffix = Math.random().toString(16).substring(2, 8); const helpText = computed<string | undefined>( - () => props.parameter["help_text"] + () => props.parameter["help_text"], ); const parameterType = computed<string>(() => props.parameter["type"]); const icon = computed<string | undefined>(() => props.parameter["fa_icon"]); const description = computed<string>(() => props.parameter["description"]); -const defaultValue = computed<string | undefined>(() => - props.parameter["default"]?.toString() +const defaultValue = computed<string | undefined>( + () => props.parameter["default"]?.toString(), ); -const enumValues = computed<string[] | undefined>(() => - props.parameter["enum"]?.map((val: string) => val.toString()) +const enumValues = computed<string[] | undefined>( + () => props.parameter["enum"]?.map((val: string) => val.toString()), ); const hidden = computed<boolean>(() => props.parameter["hidden"] ?? false); const parameterPattern = computed<string | undefined>( - () => props.parameter["pattern"] + () => props.parameter["pattern"], ); const showRightColum = computed<boolean>( () => helpText.value != undefined || props.required || - defaultValue.value != undefined + defaultValue.value != undefined, ); </script> @@ -74,7 +74,7 @@ const showRightColum = computed<boolean>( data-bs-toggle="collapse" :data-bs-target="'#helpCollapse' + randomIDSuffix" aria-expanded="false" - aria-controls="collapseExample" + :aria-controls="'helpCollapse' + randomIDSuffix" v-if="helpText" > <font-awesome-icon icon="fa-solid fa-circle-info" /> @@ -109,15 +109,13 @@ const showRightColum = computed<boolean>( >required</span > </div> - <div - class="collapse p-2 pb-0 border rounded m-2 flex-shrink-1" - :id="'helpCollapse' + randomIDSuffix" - v-if="helpText" - > - <markdown-renderer class="helpTextCode" :markdown="helpText" /> - <span v-if="parameterPattern" class="mb-2" - >Pattern: <code>{{ parameterPattern }}</code></span - > + <div class="collapse" :id="'helpCollapse' + randomIDSuffix" v-if="helpText"> + <div class="p-2 pb-0 border rounded m-2 flex-shrink-1"> + <markdown-renderer class="helpTextCode" :markdown="helpText" /> + <span v-if="parameterPattern" class="mb-2" + >Pattern: <code>{{ parameterPattern }}</code></span + > + </div> </div> </div> </template> diff --git a/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue b/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue index f8cc45c630c199a6ac292be346af74cfc8c4bdb9..93b8b9416f0f5379d7a69808a71d98c4129cdf4b 100644 --- a/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue +++ b/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue @@ -23,11 +23,11 @@ const description = computed<string>(() => props.parameterGroup["description"]); const groupHidden = computed<boolean>(() => Object.keys(parameters.value).reduce( (acc: boolean, val: string) => acc && parameters.value[val]["hidden"], - true - ) + true, + ), ); const parameters = computed<Record<string, never>>( - () => props.parameterGroup["properties"] + () => props.parameterGroup["properties"], ); </script> diff --git a/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue b/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue index 54569ffe1b4664d3dbb686047f3264bb70faef88..a3db905214f3d271a513b256fcf681c91516d878 100644 --- a/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue +++ b/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue @@ -26,7 +26,7 @@ const randomIDSuffix = Math.random().toString(16).substring(2, 8); const helpTextPresent = computed<boolean>(() => props.parameter["help_text"]); const defaultValue = computed<boolean>( - () => props.parameter["default"] ?? false + () => props.parameter["default"] ?? false, ); const emit = defineEmits<{ diff --git a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue index 34bab4686b68ab76d261b9a2807446c3a343af8b..f84a78da9d0b2b993fa901f47ac6a6eede04327f 100644 --- a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue +++ b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue @@ -30,25 +30,25 @@ const description = computed<string>(() => props.parameterGroup["description"]); const groupHidden = computed<boolean>(() => Object.keys(parameters.value).reduce( (acc: boolean, val: string) => acc && parameters.value[val]["hidden"], - true - ) + true, + ), ); const parameters = computed<Record<string, never>>( - () => props.parameterGroup["properties"] + () => props.parameterGroup["properties"], ); const formInput = computed(() => props.modelValue); const emit = defineEmits<{ ( e: "update:modelValue", - value: Record<string, number | string | boolean | undefined> + value: Record<string, number | string | boolean | undefined>, ): void; }>(); function parameterRequired( // eslint-disable-next-line @typescript-eslint/no-explicit-any parameterGroup: Record<string, any>, - parameterName: string + parameterName: string, ): boolean { return ( parameterGroup["required"]?.includes(parameterName) || // parameter is required @@ -66,7 +66,7 @@ watch( }, { deep: true, - } + }, ); </script> @@ -144,7 +144,7 @@ watch( data-bs-toggle="collapse" :data-bs-target="'#helpCollapse' + parameterName" aria-expanded="false" - aria-controls="collapseExample" + :aria-controls="'helpCollapse' + parameterName" > <font-awesome-icon class="cursor-pointer" @@ -152,21 +152,23 @@ watch( /> </span> </div> - <label v-if="parameter['description']" - ><markdown-renderer :markdown="parameter['description']" - /></label> + <label v-if="parameter['description']"> + <markdown-renderer :markdown="parameter['description']" /> + </label> <div - class="collapse p-2 pb-0 mx-2 mb-2 mt-1 flex-shrink-1 border rounded" + class="collapse" :id="'helpCollapse' + parameterName" v-if="parameter['help_text']" > - <markdown-renderer - class="helpTextCode" - :markdown="parameter['help_text']" - /> - <p v-if="parameter['pattern']"> - Pattern: <code>{{ parameter["pattern"] }}</code> - </p> + <div class="p-2 pb-0 mx-2 mb-2 mt-1 flex-shrink-1 border rounded"> + <markdown-renderer + class="helpTextCode" + :markdown="parameter['help_text']" + /> + <p v-if="parameter['pattern']"> + Pattern: <code>{{ parameter["pattern"] }}</code> + </p> + </div> </div> </template> </template> @@ -178,6 +180,7 @@ watch( div.card-body { backdrop-filter: brightness(1.2); } + span.cursor-pointer:hover { color: var(--bs-info); background: var(--bs-light); diff --git a/src/components/parameter-schema/form-mode/ParameterStringInput.vue b/src/components/parameter-schema/form-mode/ParameterStringInput.vue index 7019b22ba07eb86d7c5fb57cb0e0dbb25d5b1182..1ca1b8b9e07035506376f1b74164e462c967acd5 100644 --- a/src/components/parameter-schema/form-mode/ParameterStringInput.vue +++ b/src/components/parameter-schema/form-mode/ParameterStringInput.vue @@ -58,7 +58,7 @@ watch( if (newVal !== oldVal) { updateKeysInBucket(newVal); } - } + }, ); const pattern = computed<string>(() => props.parameter["pattern"]); @@ -72,7 +72,7 @@ const stringInput = ref<HTMLInputElement | undefined>(undefined); const format = computed<string | undefined>(() => props.parameter["format"]); const filesInBucket = computed<string[]>(() => - keysInBucket.value.filter((obj) => !obj.endsWith("/")) + keysInBucket.value.filter((obj) => !obj.endsWith("/")), ); const foldersInBucket = computed<string[]>(() => @@ -82,15 +82,15 @@ const foldersInBucket = computed<string[]>(() => return parts .slice(0, parts.length - 1) .map((part, index) => - parts.slice(0, index + 1).reduce((acc, val) => `${acc}/${val}`) + parts.slice(0, index + 1).reduce((acc, val) => `${acc}/${val}`), ); }) .flat() - .filter((val, index, array) => array.indexOf(val) === index) + .filter((val, index, array) => array.indexOf(val) === index), ); const filesAndFoldersInBucket = computed<string[]>(() => - filesInBucket.value.concat(foldersInBucket.value) + filesInBucket.value.concat(foldersInBucket.value), ); const keyDataList = computed<string[]>(() => { @@ -112,12 +112,12 @@ function updateValue() { "update:modelValue", !s3Path.bucket && s3Path.key ? undefined - : `s3://${s3Path.bucket}/${s3Path.key ?? ""}` + : `s3://${s3Path.bucket}/${s3Path.key ?? ""}`, ); } else { emit( "update:modelValue", - stringInput.value?.value ? stringInput.value?.value : undefined + stringInput.value?.value ? stringInput.value?.value : undefined, ); } } diff --git a/src/components/workflows/WorkflowCard.vue b/src/components/workflows/WorkflowCard.vue index c86514edcb40f14a2bc75094847a7abbe636271b..61f2beb88232deac2326c7a36efedb60c38f3074 100644 --- a/src/components/workflows/WorkflowCard.vue +++ b/src/components/workflows/WorkflowCard.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import type { WorkflowOut, WorkflowVersionReduced } from "@/client/workflow"; +import type { WorkflowOut, WorkflowVersion } from "@/client/workflow"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import dayjs from "dayjs"; import { onMounted, ref, computed } from "vue"; @@ -13,8 +13,8 @@ const props = defineProps<{ const randomIDSuffix: string = Math.random().toString(16).substring(2, 8); const truncateDescription = ref<boolean>(true); -const latestVersion = computed<WorkflowVersionReduced | undefined>(() => - calculateLatestVersion(props.workflow.versions) +const latestVersion = computed<WorkflowVersion | undefined>(() => + calculateLatestVersion(props.workflow.versions), ); onMounted(() => { @@ -69,31 +69,34 @@ onMounted(() => { <div v-if="props.loading" class="placeholder-glow w-50"> <span class="placeholder placeholder-lg w-50 bg-success"></span> </div> - <a + <router-link v-else - :href=" - workflow.repository_url + '/tree/' + latestVersion?.git_commit_hash - " - target="_blank" + :to="{ + name: 'workflow-version', + params: { + workflowId: workflow.workflow_id, + versionId: latestVersion?.git_commit_hash, + }, + }" class="btn btn-outline-success" role="button" > <font-awesome-icon class="fs-5" icon="fa-solid fa-tag" /> {{ latestVersion?.version }} - </a> + </router-link> <div v-if="props.loading" class="placeholder-glow w-25"> <span class="placeholder w-100 bg-secondary"></span> </div> <span - v-else + v-else-if="latestVersion" :id="'creationDate-' + randomIDSuffix" data-bs-toggle="tooltip" class="align-self-end text-secondary" :data-bs-title=" - dayjs(workflow.versions[0].created_at).format('DD.MM.YYYY HH:mm:ss') + dayjs.unix(latestVersion?.created_at).format('DD.MM.YYYY HH:mm:ss') " > - {{ dayjs(latestVersion?.created_at).fromNow() }} + {{ dayjs.unix(latestVersion?.created_at).fromNow() }} </span> </div> </div> @@ -112,7 +115,7 @@ onMounted(() => { } .icon { - max-height: 30px; - max-width: 30px; + max-height: 32px; + max-width: 32px; } </style> diff --git a/src/components/workflows/WorkflowDocumentationTabs.vue b/src/components/workflows/WorkflowDocumentationTabs.vue index 6fd048de6e9edf84526d700274baef99a92e6d34..44a1c9a3f593689729d3e9c60cfd7d9329f3d16d 100644 --- a/src/components/workflows/WorkflowDocumentationTabs.vue +++ b/src/components/workflows/WorkflowDocumentationTabs.vue @@ -16,7 +16,7 @@ const props = defineProps<{ }>(); const activeTab = computed<string>( - () => (route.query["tab"] as string) ?? "description" + () => (route.query["tab"] as string) ?? "description", ); </script> diff --git a/src/components/workflows/WorkflowStatisticsChart.vue b/src/components/workflows/WorkflowStatisticsChart.vue index c9d5e5f5a30098b0c048b1569e8b361b85247406..ee4711cfc8829daa8390973a303b1af6aaf1da07 100644 --- a/src/components/workflows/WorkflowStatisticsChart.vue +++ b/src/components/workflows/WorkflowStatisticsChart.vue @@ -35,7 +35,7 @@ Chart.register( TimeSeriesScale, LineController, LineElement, - PointElement + PointElement, ); // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -57,7 +57,7 @@ function updateData(chart: Chart, newStats: WorkflowStatistic[]) { ( (sum) => (value) => (sum += value.count) - )(0) + )(0), ); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -97,7 +97,7 @@ const paddedStats = computed<WorkflowStatistic[]>(() => { firstDay = lastYear; // Find index in stats list where the stat is from a day after the same day last year statsIndex = props.stats.findIndex((stat) => - lastYear.isSameOrBefore(dayjs(stat.day), "day") + lastYear.isSameOrBefore(dayjs(stat.day), "day"), ); } } else { @@ -229,7 +229,7 @@ onMounted(() => { // @ts-ignore tempChart.chart.options.scales.x.time.unit = xAxisLabelUnit( tempChart.chart.scales.x.min, - tempChart.chart.scales.x.max + tempChart.chart.scales.x.max, ); tempChart.chart.update(); disableZoomReset.value = !tempChart.chart.isZoomedOrPanned(); diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue index 6c39b9a2e6758e7dd0c37ed31269df6ef6ef0d50..0e72ccb8b29d9f807d5b2fc914dc1ca03d005460 100644 --- a/src/components/workflows/WorkflowWithVersionsCard.vue +++ b/src/components/workflows/WorkflowWithVersionsCard.vue @@ -1,28 +1,47 @@ <script setup lang="ts"> -import type { WorkflowOut } from "@/client/workflow"; -import { ref } from "vue"; +import type { WorkflowOut, WorkflowVersion } from "@/client/workflow"; +import { onMounted, ref, watch } from "vue"; import { Status } from "@/client/workflow"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import dayjs from "dayjs"; import { sortedVersions } from "@/utils/Workflow"; +import { Tooltip } from "bootstrap"; const props = defineProps<{ workflow: WorkflowOut; loading: boolean; }>(); const truncateDescription = ref<boolean>(true); +const randomIDSuffix: string = Math.random().toString(16).substring(2, 8); const emit = defineEmits<{ (e: "workflow-update-click", workflow: WorkflowOut): void; (e: "workflow-delete-click", workflow: WorkflowOut): void; + (e: "workflow-update-credentials-click", workflow: WorkflowOut): void; + (e: "workflow-update-icon-click", version: WorkflowVersion): void; }>(); +watch( + () => props.loading, + (loading) => { + if (!loading && props.workflow.private) { + new Tooltip(`#tooltip-${randomIDSuffix}`); + } + }, +); + const statusToIconMapping: Record<string, string> = { PUBLISHED: "fa-solid fa-circle-check", DENIED: "fa-solid fa-x", CREATED: "fa-solid fa-circle-pause", DEPRECATED: "fa-solid fa-box-archive", }; + +onMounted(() => { + if (!props.loading && props.workflow.private) { + new Tooltip(`#tooltip-${randomIDSuffix}`); + } +}); </script> <template> @@ -34,8 +53,28 @@ const statusToIconMapping: Record<string, string> = { <div v-if="props.loading" class="placeholder-glow w-100"> <span class="placeholder col-6"></span> </div> - <span v-else class="text-truncate">{{ props.workflow.name }}</span> + <div v-else class="text-truncate"> + <font-awesome-icon + v-if="props.workflow.private" + icon="fa-solid fa-lock" + class="fs-5 me-2 tooltip-private-repository" + :id="'tooltip-' + randomIDSuffix" + data-bs-toggle="tooltip" + data-bs-title="Private Git Repository" + /> + <span>{{ props.workflow.name }}</span> + </div> <div> + <button + type="button" + class="btn btn-outline-info me-2" + @click="emit('workflow-update-credentials-click', props.workflow)" + :class="{ disabled: props.loading }" + data-bs-toggle="modal" + data-bs-target="#updateWorkflowCredentialsModal" + > + <font-awesome-icon icon="fa-solid fa-key" /> + </button> <button type="button" class="btn btn-outline-danger me-2" @@ -85,13 +124,6 @@ const statusToIconMapping: Record<string, string> = { v-for="version in sortedVersions(props.workflow.versions)" :key="version.git_commit_hash" > - <td class="w-fit"> - <img - v-if="version.icon_url != null" - :src="version.icon_url" - alt="Workflow Version Icon" - /> - </td> <th scope="row" class="fw-bold">{{ version.version }}</th> <td :class="{ @@ -106,7 +138,28 @@ const statusToIconMapping: Record<string, string> = { /> {{ version.status }} </td> - <td>{{ dayjs(version.created_at).format("D MMMM YYYY") }}</td> + <td> + {{ dayjs.unix(version.created_at).format("D MMMM YYYY") }} + </td> + <td class="w-fit"> + <img + v-if="version.icon_url != null" + :src="version.icon_url" + alt="Workflow Version Icon" + @click="emit('workflow-update-icon-click', version)" + class="cursor-pointer" + data-bs-toggle="modal" + data-bs-target="#updateWorkflowVersionIconModal" + /> + <font-awesome-icon + v-else + icon="fa-solid fa-circle-plus" + class="add-icon-hover cursor-pointer" + @click="emit('workflow-update-icon-click', version)" + data-bs-toggle="modal" + data-bs-target="#updateWorkflowVersionIconModal" + /> + </td> <td> <router-link class="w-fit mx-0" @@ -117,8 +170,8 @@ const statusToIconMapping: Record<string, string> = { versionId: version.git_commit_hash, }, }" - >View</router-link - > + >View + </router-link> </td> </tr> </tbody> @@ -145,8 +198,13 @@ const statusToIconMapping: Record<string, string> = { transform: translate(0, -5px); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } + td > img { max-width: 1em; max-height: 1em; } + +.add-icon-hover:hover { + color: var(--bs-success) !important; +} </style> diff --git a/src/components/workflows/modals/ArbitraryWorkflowModal.vue b/src/components/workflows/modals/ArbitraryWorkflowModal.vue index ef8967082f11f868d39d73ec83c34553d92f9a11..9abbaf2783fa0d6868361493de778f1114a70f86 100644 --- a/src/components/workflows/modals/ArbitraryWorkflowModal.vue +++ b/src/components/workflows/modals/ArbitraryWorkflowModal.vue @@ -1,20 +1,22 @@ <script setup lang="ts"> import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; -import { computed, onMounted, reactive, ref } from "vue"; +import { computed, onMounted, reactive, ref, watch } from "vue"; import { useRouter } from "vue-router"; import { GitRepository, requiredRepositoryFiles, determineGitIcon, } from "@/utils/GitRepository"; -import { Modal } from "bootstrap"; +import { Collapse, Modal } from "bootstrap"; const props = defineProps<{ modalID: string; }>(); let createWorkflowModal: Modal | null = null; +let privateRepositoryCollapse: Collapse | null = null; +let tokenHelpCollapse: Collapse | null = null; const arbitraryWorkflowForm = ref<HTMLFormElement | undefined>(undefined); const workflowRepositoryElement = ref<HTMLInputElement | undefined>(undefined); const router = useRouter(); @@ -27,6 +29,14 @@ const workflow = reactive<{ git_commit_hash: "", }); +const repositoryCredentials = reactive<{ + token: string; + privateRepo: boolean; +}>({ + token: "", + privateRepo: false, +}); + const formState = reactive<{ loading: boolean; checkRepoLoading: boolean; @@ -43,18 +53,34 @@ const formState = reactive<{ unsupportedRepository: false, }); +watch( + () => repositoryCredentials.privateRepo, + (show) => { + if (show) { + privateRepositoryCollapse?.show(); + } else { + privateRepositoryCollapse?.hide(); + tokenHelpCollapse?.hide(); + } + }, +); + function modalClosed() { formState.validated = false; + tokenHelpCollapse?.hide(); } function viewWorkflow() { - console.log("View", workflow); createWorkflowModal?.hide(); router.push({ name: "arbitrary-workflow", query: { repository: encodeURI(workflow.repository_url), commit_hash: workflow.git_commit_hash, + token: + repositoryCredentials.token.length > 0 + ? encodeURIComponent(repositoryCredentials.token) + : undefined, }, }); } @@ -68,7 +94,10 @@ function checkRepository() { try { const repo = GitRepository.buildRepository( workflow.repository_url, - workflow.git_commit_hash + workflow.git_commit_hash, + repositoryCredentials.privateRepo + ? repositoryCredentials.token + : undefined, ); repo .checkFilesExist(requiredRepositoryFiles, true) @@ -80,7 +109,7 @@ function checkRepository() { // Allow execution of the workflow if main.nf and parameter schema are not missing if ( formState.missingFiles.findIndex( - (file) => file === "main.nf" || file === "nextflow_schema.json" + (file) => file === "main.nf" || file === "nextflow_schema.json", ) < 0 ) { formState.allowUpload = true; @@ -89,17 +118,24 @@ function checkRepository() { } catch (e) { formState.unsupportedRepository = true; workflowRepositoryElement.value?.setCustomValidity( - "Repository is not supported" + "Repository is not supported", ); } } } + const gitIcon = computed<string>(() => - determineGitIcon(workflow.repository_url) + determineGitIcon(workflow.repository_url), ); onMounted(() => { createWorkflowModal = new Modal("#" + props.modalID); + privateRepositoryCollapse = new Collapse("#privateRepositoryCollapse", { + toggle: false, + }); + tokenHelpCollapse = new Collapse("#tokenHelpCollapse", { + toggle: false, + }); }); </script> @@ -177,6 +213,73 @@ onMounted(() => { </li> </ul> </div> + <div class="mb-3"> + <div class="form-check fs-5"> + <input + class="form-check-input" + type="checkbox" + v-model="repositoryCredentials.privateRepo" + id="privateRepositoryCheckbox" + @change="formState.allowUpload = false" + aria-controls="#privateRepositoryCollapse" + /> + <label class="form-check-label" for="privateRepositoryCheckbox"> + Private Git Repository + </label> + </div> + <div class="collapse" id="privateRepositoryCollapse"> + <label for="tokenUsernameInput" class="form-label">Token</label> + <div class="input-group"> + <div class="input-group-text"> + <font-awesome-icon icon="fa-solid fa-key" /> + </div> + <input + type="password" + class="form-control" + id="repositoryTokenInput" + v-model="repositoryCredentials.token" + @change="formState.allowUpload = false" + :required="repositoryCredentials.privateRepo" + aria-controls="#tokenHelpCollapse" + /> + <div + class="input-group-text cursor-pointer hover-info" + @click="tokenHelpCollapse?.toggle()" + > + <font-awesome-icon icon="fa-solid fa-circle-question" /> + </div> + </div> + <div class="collapse" id="tokenHelpCollapse"> + <div class="card card-body mt-3"> + <h5>GitHub</h5> + <p> + For private GitHub repositories, CloWM needs a Personal Access + Token (classic) with the scope <code>repo</code>.<br /> + Read this + <a + target="_blank" + href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic" + >Tutorial</a + > + on how to create such a token. + </p> + <h5>GitLab</h5> + <p> + For private GitLab repositories, CloWM needs a Project Access + Token with the <code>read_api</code> scope and at least + <code>Reporter</code> role.<br /> + Read this + <a + target="_blank" + href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token" + >Tutorial</a + > + on how to create such a token. + </p> + </div> + </div> + </div> + </div> </form> </template> <template v-slot:footer> @@ -216,4 +319,8 @@ onMounted(() => { </bootstrap-modal> </template> -<style scoped></style> +<style scoped> +.hover-info:hover { + color: var(--bs-info) !important; +} +</style> diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue index 238da5033b08939566f1086995cea99706e57977..3971a16223b875c5fb295d56bcaf601f7b02c631 100644 --- a/src/components/workflows/modals/CreateWorkflowModal.vue +++ b/src/components/workflows/modals/CreateWorkflowModal.vue @@ -1,10 +1,7 @@ <script setup lang="ts"> -import { computed, onMounted, reactive, ref } from "vue"; -import { Modal, Toast } from "bootstrap"; -import type { - Body_Workflow_create_workflow, - WorkflowOut, -} from "@/client/workflow"; +import { computed, onMounted, reactive, ref, watch } from "vue"; +import { Modal, Toast, Collapse, Tooltip } from "bootstrap"; +import type { WorkflowIn, WorkflowOut } from "@/client/workflow"; import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import { ApiError, WorkflowService } from "@/client/workflow"; @@ -31,14 +28,15 @@ const props = defineProps<{ // ============================================================================= let createWorkflowModal: Modal | null = null; let successToast: Toast | null = null; +let privateRepositoryCollapse: Collapse | null = null; +let tokenHelpCollapse: Collapse | null = null; // HTML Form Elements // ============================================================================= const workflowCreateForm = ref<HTMLFormElement | undefined>(undefined); -const workflowIconInput = ref<HTMLInputElement | undefined>(undefined); const workflowVersionElement = ref<HTMLInputElement | undefined>(undefined); const workflowGitCommitHashElement = ref<HTMLInputElement | undefined>( - undefined + undefined, ); const workflowNameElement = ref<HTMLInputElement | undefined>(undefined); const workflowRepositoryElement = ref<HTMLInputElement | undefined>(undefined); @@ -49,13 +47,14 @@ const randomIDSuffix = Math.random().toString(16).substring(2, 8); // Reactive State // ============================================================================= -const workflow = reactive<Body_Workflow_create_workflow>({ - icon: undefined, +const workflow = reactive<WorkflowIn>({ name: "", short_description: "", repository_url: "", git_commit_hash: "", initial_version: undefined, + token: undefined, + modes: [], }); const formState = reactive<{ @@ -74,10 +73,30 @@ const formState = reactive<{ unsupportedRepository: false, }); +const repositoryCredentials = reactive<{ + token: string; + privateRepo: boolean; +}>({ + token: "", + privateRepo: false, +}); + // Computed Properties // ============================================================================= const gitIcon = computed<string>(() => - determineGitIcon(workflow.repository_url) + determineGitIcon(workflow.repository_url), +); + +watch( + () => repositoryCredentials.privateRepo, + (show) => { + if (show) { + privateRepositoryCollapse?.show(); + } else { + privateRepositoryCollapse?.hide(); + tokenHelpCollapse?.hide(); + } + }, ); // Functions @@ -90,6 +109,7 @@ function modalClosed() { workflowGitCommitHashElement.value?.setCustomValidity(""); workflowRepositoryElement.value?.setCustomValidity(""); workflowNameElement.value?.setCustomValidity(""); + tokenHelpCollapse?.hide(); } /** @@ -104,6 +124,12 @@ function createWorkflow() { formState.loading = true; workflowNameElement.value?.setCustomValidity(""); workflowGitCommitHashElement.value?.setCustomValidity(""); + if ( + repositoryCredentials.privateRepo && + repositoryCredentials.token.length > 0 + ) { + workflow.token = repositoryCredentials.token; + } WorkflowService.workflowCreateWorkflow(workflow) .then((w) => { emit("workflow-created", w); @@ -117,7 +143,7 @@ function createWorkflow() { workflowNameElement.value?.setCustomValidity("Name is already taken"); } else if (errorText.startsWith("Workflow with git_commit_hash")) { workflowGitCommitHashElement.value?.setCustomValidity( - "Git commit is already used by a workflow" + "Git commit is already used by a workflow", ); } }) @@ -132,38 +158,41 @@ function createWorkflow() { */ function resetForm() { modalClosed(); - workflow.icon = undefined; workflow.name = ""; workflow.short_description = ""; workflow.repository_url = ""; workflow.git_commit_hash = ""; workflow.initial_version = undefined; - if (workflowIconInput.value != undefined) { - workflowIconInput.value.value = ""; - } + workflow.token = undefined; + repositoryCredentials.privateRepo = false; + repositoryCredentials.token = ""; + privateRepositoryCollapse?.hide(); } /** * Watcher function for the file upload in the form. */ -function iconChanged() { - workflow.icon = workflowIconInput.value?.files?.[0].slice(); -} +//function iconChanged() { +// workflow.icon = workflowIconInput.value?.files?.[0].slice(); +//} /** * Check the workflow repository for the necessary files. */ function checkRepository() { formState.validated = true; + workflowRepositoryElement.value?.setCustomValidity(""); + workflowGitCommitHashElement.value?.setCustomValidity(""); if (workflowCreateForm.value?.checkValidity() && !formState.allowUpload) { formState.unsupportedRepository = false; formState.missingFiles = []; - workflowRepositoryElement.value?.setCustomValidity(""); - workflowGitCommitHashElement.value?.setCustomValidity(""); try { const repo = GitRepository.buildRepository( workflow.repository_url, - workflow.git_commit_hash + workflow.git_commit_hash, + repositoryCredentials.privateRepo + ? repositoryCredentials.token + : undefined, ); repo .checkFilesExist(requiredRepositoryFiles, true) @@ -173,13 +202,13 @@ function checkRepository() { .catch((e: Error) => { formState.missingFiles = e.message.split(","); workflowGitCommitHashElement.value?.setCustomValidity( - "Files are missing in the repository" + "Files are missing in the repository", ); }); } catch (e) { formState.unsupportedRepository = true; workflowRepositoryElement.value?.setCustomValidity( - "Repository is not supported" + "Repository is not supported", ); } } @@ -191,7 +220,7 @@ function checkRepository() { function checkVersionValidity() { if (valid(workflow.initial_version) == null) { workflowVersionElement.value?.setCustomValidity( - "Please use semantic versioning" + "Please use semantic versioning", ); } else { workflowVersionElement.value?.setCustomValidity(""); @@ -203,6 +232,15 @@ function checkVersionValidity() { onMounted(() => { createWorkflowModal = new Modal("#" + props.modalID); successToast = new Toast("#successToast-" + randomIDSuffix); + privateRepositoryCollapse = new Collapse("#privateRepositoryCollapse", { + toggle: false, + }); + tokenHelpCollapse = new Collapse("#tokenHelpCollapse", { + toggle: false, + }); + new Tooltip("#tooltip-version-" + randomIDSuffix); + new Tooltip("#tooltip-commit-" + randomIDSuffix); + new Tooltip("#tooltip-url-" + randomIDSuffix); }); </script> @@ -272,10 +310,7 @@ onMounted(() => { placeholder="Describe the purpose of the workflow in a few words." ></textarea> <div class="invalid-feedback"> - Requirements - <ul class="mb-0"> - <li>At least 64 Characters long</li> - </ul> + Description needs to be at least 64 characters long. </div> </div> </div> @@ -298,9 +333,17 @@ onMounted(() => { @change="formState.allowUpload = false" aria-describedby="gitRepoProviderHelp" /> + <div + class="input-group-text hover-info" + :id="'tooltip-url-' + randomIDSuffix" + data-bs-toggle="tooltip" + data-bs-title="The URL of the git repository containing the workflow" + > + <font-awesome-icon icon="fa-solid fa-circle-question" /> + </div> </div> <div id="gitRepoProviderHelp" class="form-text"> - We support Github and GitLab Repositories + We support GitHub and GitLab Repositories </div> <div class="text-danger"> <div v-if="formState.unsupportedRepository"> @@ -308,37 +351,45 @@ onMounted(() => { </div> </div> </div> - <div class="mb-3"> - <label for="workflowGitCommitInput" class="form-label" - >Git Commit Hash</label - > - <div class="input-group"> - <div class="input-group-text"> - <font-awesome-icon icon="fa-solid fa-code-commit" /> + <div class="row mb-3"> + <div class="col-8"> + <label for="workflowGitCommitInput" class="form-label" + >Git Commit Hash</label + > + <div class="input-group"> + <div class="input-group-text"> + <font-awesome-icon icon="fa-solid fa-code-commit" /> + </div> + <input + type="text" + class="form-control text-lowercase" + id="workflowGitCommitInput" + placeholder="ba8bcd9..." + required + ref="workflowGitCommitHashElement" + maxlength="40" + pattern="[0-9a-f]{40}" + v-model="workflow.git_commit_hash" + @change="formState.allowUpload = false" + /> + <div + class="input-group-text hover-info" + :id="'tooltip-commit-' + randomIDSuffix" + data-bs-toggle="tooltip" + data-bs-title="Hash of the Git commit used for the initial version" + > + <font-awesome-icon icon="fa-solid fa-circle-question" /> + </div> + </div> + <div v-if="formState.missingFiles.length > 0" class="text-danger"> + The following files are missing in the repository + <ul> + <li v-for="file in formState.missingFiles" :key="file"> + {{ file }} + </li> + </ul> </div> - <input - type="text" - class="form-control text-lowercase" - id="workflowGitCommitInput" - placeholder="ba8bcd9..." - required - ref="workflowGitCommitHashElement" - maxlength="40" - pattern="[0-9a-f]{40}" - v-model="workflow.git_commit_hash" - @change="formState.allowUpload = false" - /> </div> - </div> - <div v-if="formState.missingFiles.length > 0" class="text-danger"> - The following files are missing in the repository - <ul> - <li v-for="file in formState.missingFiles" :key="file"> - {{ file }} - </li> - </ul> - </div> - <div class="row mb-3"> <div class="col-4"> <label for="workflowVersionInput" class="form-label" >Initial Version</label @@ -357,20 +408,86 @@ onMounted(() => { @change="checkVersionValidity" v-model="workflow.initial_version" /> + <div + class="input-group-text hover-info" + :id="'tooltip-version-' + randomIDSuffix" + data-bs-toggle="tooltip" + data-bs-title="Should follow semantic versioning" + > + <font-awesome-icon icon="fa-solid fa-circle-question" /> + </div> </div> </div> - <div class="col-8"> - <label for="workflowIconInput" class="form-label" - >Optional Icon</label - > + </div> + <div class="mb-3"> + <div class="form-check fs-5"> <input - type="file" - ref="workflowIconInput" - accept="image/*" - class="form-control" - id="workflowIconInput" - @change="iconChanged" + class="form-check-input" + type="checkbox" + v-model="repositoryCredentials.privateRepo" + id="privateRepositoryCheckbox" + @change="formState.allowUpload = false" + aria-controls="#privateRepositoryCollapse" /> + <label class="form-check-label" for="privateRepositoryCheckbox"> + Private Git Repository + </label> + </div> + <div class="collapse" id="privateRepositoryCollapse"> + <label for="tokenUsernameInput" class="form-label">Token</label> + <div class="input-group"> + <div class="input-group-text"> + <font-awesome-icon icon="fa-solid fa-key" /> + </div> + <input + type="password" + class="form-control" + id="repositoryTokenInput" + v-model="repositoryCredentials.token" + @change="formState.allowUpload = false" + :required="repositoryCredentials.privateRepo" + aria-controls="#tokenHelpCollapse" + /> + <div + class="input-group-text cursor-pointer hover-info" + @click="tokenHelpCollapse?.toggle()" + > + <font-awesome-icon icon="fa-solid fa-circle-question" /> + </div> + </div> + <div class="collapse" id="tokenHelpCollapse"> + <div class="card card-body mt-3"> + <h5>GitHub</h5> + <p> + For private GitHub repositories, CloWM needs a Personal Access + Token (classic) with the scope <code>repo</code>.<br /> + Read this + <a + target="_blank" + href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic" + >Tutorial</a + > + on how to create such a token. + </p> + <h5>GitLab</h5> + <p> + For private GitLab repositories, CloWM needs a Project Access + Token with the <code>read_api</code> scope and at least + <code>Reporter</code> role.<br /> + Read this + <a + target="_blank" + href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token" + >Tutorial</a + > + on how to create such a token. + </p> + <p> + Select a distant expiration date for both providers to ensure + that there won't be any problems in the short future. + </p> + </div> + </div> </div> </div> </form> @@ -412,4 +529,8 @@ onMounted(() => { </bootstrap-modal> </template> -<style scoped></style> +<style scoped> +.hover-info:hover { + color: var(--bs-info) !important; +} +</style> diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..df4ddd1b27e32884107fed0d40de7ac16d7d2f6f --- /dev/null +++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue @@ -0,0 +1,285 @@ +<script setup lang="ts"> +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; +import type { WorkflowCredentialsIn, WorkflowOut } from "@/client/workflow"; +import { WorkflowCredentialsService } from "@/client/workflow"; +import { onMounted, ref, reactive } from "vue"; +import { Modal, Toast } from "bootstrap"; +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; +import DeleteModal from "@/components/modals/DeleteModal.vue"; +import { GitRepository } from "@/utils/GitRepository"; + +// Constants +// ============================================================================= +const randomIDSuffix = Math.random().toString(16).substring(2, 8); + +// Bootstrap Elements +// ============================================================================= +let updateCredentialsModal: Modal | null = null; +let successToast: Toast | null = null; + +// Form Elements +// ============================================================================= +const credentialsUpdateForm = ref<HTMLFormElement | undefined>(undefined); +const credentialsInputElement = ref<HTMLInputElement | undefined>(undefined); + +// Props +// ============================================================================= +const props = defineProps<{ + modalID: string; + workflow: WorkflowOut; +}>(); + +// Reactive State +// ============================================================================= +const credentials = reactive<WorkflowCredentialsIn>({ + token: "", +}); + +const formState = reactive<{ + loading: boolean; + validated: boolean; + updateCredentials: boolean; + error: boolean; +}>({ + loading: false, + validated: false, + updateCredentials: true, + error: false, +}); + +// Events +// ============================================================================= +const emit = defineEmits<{ + ( + e: "credentials-updated", + workflow: WorkflowOut, + credentialsDeleted: boolean, + ): void; +}>(); + +// Functions +// ============================================================================= +function resetForm() { + credentials.token = ""; +} + +function modalClosed() { + formState.validated = false; + credentialsInputElement.value?.setCustomValidity(""); + formState.error = false; + resetForm(); +} + +function updateCredentials() { + formState.validated = true; + formState.error = false; + credentialsInputElement.value?.setCustomValidity(""); + if (credentialsUpdateForm.value?.checkValidity()) { + const repo = GitRepository.buildRepository( + props.workflow.repository_url, + props.workflow.versions[0].git_commit_hash, + credentials.token, + ); + repo.checkFileExist("main.nf").then((result: boolean) => { + if (result) { + WorkflowCredentialsService.workflowCredentialsUpdateWorkflowCredentials( + props.workflow.workflow_id, + credentials, + ) + .then(() => { + formState.updateCredentials = true; + emit("credentials-updated", props.workflow, false); + successToast?.show(); + updateCredentialsModal?.hide(); + }) + .catch((error) => { + console.error(error); + }) + .finally(() => { + formState.loading = false; + }); + } else { + formState.error = true; + credentialsInputElement.value?.setCustomValidity( + "Can't access repository.", + ); + formState.loading = false; + } + }); + formState.loading = true; + } +} + +function deleteCredentials() { + formState.loading = true; + WorkflowCredentialsService.workflowCredentialsDeleteWorkflowCredentials( + props.workflow.workflow_id, + ) + .then(() => { + formState.updateCredentials = false; + emit("credentials-updated", props.workflow, true); + successToast?.show(); + updateCredentialsModal?.hide(); + }) + .catch((error) => { + console.error(error); + }) + .finally(() => { + formState.loading = false; + }); +} + +// Lifecycle Events +// ============================================================================= +onMounted(() => { + updateCredentialsModal = new Modal("#" + props.modalID); + successToast = new Toast("#successToast-" + randomIDSuffix); +}); +</script> + +<template> + <DeleteModal + v-if="props.workflow.private" + :modalID="'delete-credentials-modal' + randomIDSuffix" + :object-name-delete="'credentials for workflow ' + props.workflow.name" + :back-modal-id="modalID" + @confirm-delete="deleteCredentials()" + /> + <div class="toast-container position-fixed top-toast end-0 p-3"> + <div + role="alert" + aria-live="assertive" + aria-atomic="true" + class="toast text-bg-success align-items-center border-0" + data-bs-autohide="true" + :id="'successToast-' + randomIDSuffix" + > + <div class="d-flex"> + <div v-if="formState.updateCredentials" class="toast-body"> + Successfully updated credentials + </div> + <div v-else class="toast-body">Successfully deleted credentials</div> + <button + type="button" + class="btn-close btn-close-white me-2 m-auto" + data-bs-dismiss="toast" + aria-label="Close" + ></button> + </div> + </div> + </div> + <bootstrap-modal + :modalID="modalID" + :static-backdrop="true" + modal-label="Update Workflow Version Icon Modal" + v-on="{ 'hidden.bs.modal': modalClosed }" + > + <template v-slot:header> + Update git repository credentials for Workflow + <span class="fw-bold">{{ props.workflow.name }}</span> + </template> + <template v-slot:extra-button v-if="props.workflow.private"> + <button + class="btn delete-icon" + data-bs-toggle="modal" + :data-bs-target="'#delete-credentials-modal' + randomIDSuffix" + > + <font-awesome-icon icon="fa-solid fa-trash" /> + </button> + </template> + <template v-slot:body> + <form + ref="credentialsUpdateForm" + id="credentialsUpdateForm" + :class="{ 'was-validated': formState.validated }" + > + <label for="workflowCredentialsInput" class="form-label" + >New Token</label + > + <div class="input-group"> + <div class="input-group-text"> + <font-awesome-icon icon="fa-solid fa-key" /> + </div> + <input + type="password" + ref="credentialsInputElement" + class="form-control" + id="workflowCredentialsInput" + aria-describedby="iconHelp" + required + v-model="credentials.token" + /> + </div> + <div v-if="formState.error" class="text-danger"> + Can't access the repository. + </div> + <div class="card card-body mt-3"> + <h5>GitHub</h5> + <p> + For private GitHub repositories, CloWM needs a Personal Access Token + (classic) with the scope <code>repo</code>.<br /> + Read this + <a + target="_blank" + href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic" + >Tutorial</a + > + on how to create such a token. + </p> + <h5>GitLab</h5> + <p> + For private GitLab repositories, CloWM needs a Project Access Token + with the <code>read_api</code> scope and at least + <code>Reporter</code> role.<br /> + Read this + <a + target="_blank" + href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token" + >Tutorial</a + > + on how to create such a token. + </p> + <p> + Select a distant expiration date for both providers to ensure that + there won't be any problems in the short future. + </p> + </div> + </form> + </template> + <template v-slot:footer> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> + Close + </button> + <button + type="submit" + form="credentialsUpdateForm" + class="btn btn-primary" + :disabled="formState.loading" + @click.prevent="updateCredentials" + > + <span + v-if="formState.loading" + class="spinner-border spinner-border-sm" + role="status" + aria-hidden="true" + ></span> + Save + </button> + </template> + </bootstrap-modal> +</template> + +<style scoped> +img { + max-height: 64px; + max-width: 64px; +} + +.delete-icon { + color: var(--bs-secondary) !important; +} + +.delete-icon:hover { + color: var(--bs-danger) !important; +} +</style> diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue index 471d884a4e0791401b404e7ae77c2ddfc68ccb84..4ce43dc0026daf6c66241584f25372d8601187d1 100644 --- a/src/components/workflows/modals/UpdateWorkflowModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowModal.vue @@ -2,14 +2,14 @@ import { computed, onMounted, reactive, ref, watch } from "vue"; import { Modal, Toast } from "bootstrap"; import type { - Body_Workflow_update_workflow, + WorkflowUpdate, WorkflowOut, - WorkflowVersionFull, + WorkflowVersion, + ApiError, } from "@/client/workflow"; import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; -import type { WorkflowVersionReduced, ApiError } from "@/client/workflow"; -import { WorkflowService } from "@/client/workflow"; +import { WorkflowService, WorkflowCredentialsService } from "@/client/workflow"; import { GitRepository, requiredRepositoryFiles, @@ -29,7 +29,7 @@ const workflowUpdateForm = ref<HTMLFormElement | undefined>(undefined); const workflowIconInputElement = ref<HTMLInputElement | undefined>(undefined); const workflowVersionElement = ref<HTMLInputElement | undefined>(undefined); const workflowGitCommitHashElement = ref<HTMLInputElement | undefined>( - undefined + undefined, ); const workflowIconElement = ref<HTMLImageElement | undefined>(undefined); @@ -46,10 +46,11 @@ const props = defineProps<{ // Reactive State // ============================================================================= -const workflowUpdate = reactive<Body_Workflow_update_workflow>({ - icon: undefined, +const workflowUpdate = reactive<WorkflowUpdate>({ version: "", git_commit_hash: "", + delete_modes: [], + append_modes: [], }); const formState = reactive<{ @@ -58,58 +59,64 @@ const formState = reactive<{ loading: boolean; checkRepoLoading: boolean; allowUpload: boolean; + loadCredentials: boolean; + workflowToken?: string; }>({ loading: false, checkRepoLoading: false, allowUpload: false, validated: false, missingFiles: [], + loadCredentials: false, + workflowToken: undefined, }); watch( () => props.workflow, () => { resetForm(); - } + if (props.workflow.private) { + formState.loadCredentials = true; + WorkflowCredentialsService.workflowCredentialsGetWorkflowCredentials( + props.workflow.workflow_id, + ) + .then((credentials) => { + formState.workflowToken = credentials.token ?? undefined; + }) + .catch(() => { + formState.workflowToken = undefined; + }) + .finally(() => { + formState.loadCredentials = false; + }); + } else { + formState.workflowToken = undefined; + } + }, ); // Computed Properties // ============================================================================= -const latestVersion = computed<WorkflowVersionReduced>(() => { +const latestVersion = computed<WorkflowVersion>(() => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return calculateLatestVersion(props.workflow.versions)!; }); const gitIcon = computed<string>(() => - determineGitIcon(props.workflow.repository_url) + determineGitIcon(props.workflow.repository_url), ); const showIcon = computed<boolean>( - () => - latestVersion.value.icon_url != undefined || - workflowUpdate.icon != undefined + () => latestVersion.value.icon_url != undefined, ); // Emitted Events // ============================================================================= const emit = defineEmits<{ - (e: "workflow-updated", workflow: WorkflowVersionFull): void; + (e: "workflow-updated", workflow: WorkflowVersion): void; }>(); // Functions // ============================================================================= -function iconChanged() { - workflowUpdate.icon = workflowIconInputElement.value?.files?.[0].slice(); - if (workflowUpdate.icon != undefined) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - workflowIconElement.value!.src = URL.createObjectURL( - workflowUpdate.icon.slice() - ); - } else { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - workflowIconElement.value!.src = latestVersion.value.icon_url ?? ""; - } -} - function modalClosed() { formState.validated = false; formState.missingFiles = []; @@ -120,11 +127,11 @@ function modalClosed() { function checkVersionValidity() { if (valid(workflowUpdate.version) == null) { workflowVersionElement.value?.setCustomValidity( - "Please use semantic versioning" + "Please use semantic versioning", ); } else if (lte(workflowUpdate.version, latestVersion.value.version)) { workflowVersionElement.value?.setCustomValidity( - "The new version must be greater than previous version" + "The new version must be greater than previous version", ); } else { workflowVersionElement.value?.setCustomValidity(""); @@ -133,12 +140,13 @@ function checkVersionValidity() { function checkRepository() { formState.validated = true; + workflowGitCommitHashElement.value?.setCustomValidity(""); if (workflowUpdateForm.value?.checkValidity() && !formState.allowUpload) { formState.missingFiles = []; - workflowGitCommitHashElement.value?.setCustomValidity(""); const repo = GitRepository.buildRepository( props.workflow.repository_url, - workflowUpdate.git_commit_hash + workflowUpdate.git_commit_hash, + formState.workflowToken, ); repo .checkFilesExist(requiredRepositoryFiles, true) @@ -148,7 +156,7 @@ function checkRepository() { .catch((e: Error) => { formState.missingFiles = e.message.split(","); workflowGitCommitHashElement.value?.setCustomValidity( - "Files are missing in the repository" + "Files are missing in the repository", ); }); } @@ -159,7 +167,6 @@ function resetForm() { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion workflowIconElement.value!.src = latestVersion.value.icon_url ?? ""; workflowUpdate.version = ""; - workflowUpdate.icon = undefined; workflowUpdate.git_commit_hash = ""; if (workflowIconInputElement.value != undefined) { workflowIconInputElement.value.value = ""; @@ -174,7 +181,7 @@ function updateWorkflow() { workflowGitCommitHashElement.value?.setCustomValidity(""); WorkflowService.workflowUpdateWorkflow( props.workflow.workflow_id, - workflowUpdate + workflowUpdate, ) .then((version) => { emit("workflow-updated", version); @@ -186,7 +193,7 @@ function updateWorkflow() { const errorText = error.body["detail"]; if (errorText.startsWith("Workflow with git_commit_hash")) { workflowGitCommitHashElement.value?.setCustomValidity( - "Git commit is already used by a workflow" + "Git commit is already used by a workflow", ); } }) @@ -250,44 +257,49 @@ onMounted(() => { target="_blank" >{{ props.workflow.repository_url }}</a > + <font-awesome-icon + v-if="props.workflow.private" + class="ms-2" + icon="fa-solid fa-lock" + /> <img - :src="latestVersion.icon_url" + :src="latestVersion.icon_url ?? undefined" ref="workflowIconElement" class="float-end" :hidden="!showIcon" /> </div> - <div class="mb-3"> - <label for="workflowGitCommitInput" class="form-label" - >Git Commit Hash</label - > - <div class="input-group"> - <div class="input-group-text"> - <font-awesome-icon icon="fa-solid fa-code-commit" /> + <div class="row"> + <div class="col-8"> + <label for="workflowGitCommitInput" class="form-label" + >Git Commit Hash</label + > + <div class="input-group"> + <div class="input-group-text"> + <font-awesome-icon icon="fa-solid fa-code-commit" /> + </div> + <input + type="text" + class="form-control text-lowercase" + id="workflowGitCommitInput" + placeholder="ba8bcd9..." + required + ref="workflowGitCommitHashElement" + maxlength="40" + pattern="[0-9a-f]{40}" + v-model="workflowUpdate.git_commit_hash" + @change="formState.allowUpload = false" + /> + </div> + <div v-if="formState.missingFiles.length > 0" class="text-danger"> + The following files are missing in the repository + <ul> + <li v-for="file in formState.missingFiles" :key="file"> + {{ file }} + </li> + </ul> </div> - <input - type="text" - class="form-control text-lowercase" - id="workflowGitCommitInput" - placeholder="ba8bcd9..." - required - ref="workflowGitCommitHashElement" - maxlength="40" - pattern="[0-9a-f]{40}" - v-model="workflowUpdate.git_commit_hash" - @change="formState.allowUpload = false" - /> </div> - </div> - <div v-if="formState.missingFiles.length > 0" class="text-danger"> - The following files are missing in the repository - <ul> - <li v-for="file in formState.missingFiles" :key="file"> - {{ file }} - </li> - </ul> - </div> - <div class="row mb-3"> <div class="col-4"> <label for="workflowVersionInput" class="form-label">Version</label> <div class="input-group"> @@ -311,23 +323,6 @@ onMounted(() => { Previous Version: {{ latestVersion.version }} </div> </div> - <div class="col-8"> - <label for="workflowIconInput" class="form-label" - >Optional Icon</label - > - <input - type="file" - ref="workflowIconInputElement" - accept="image/*" - class="form-control" - id="workflowIconInput" - @change="iconChanged" - aria-describedby="iconHelp" - /> - <div id="iconHelp" class="form-text"> - If not set, the previous icon will be used - </div> - </div> </div> </form> </template> @@ -336,7 +331,7 @@ onMounted(() => { type="button" class="btn btn-info me-auto" @click="checkRepository" - :disabled="formState.allowUpload" + :disabled="formState.allowUpload || formState.loadCredentials" > <span v-if="formState.checkRepoLoading" diff --git a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..ede37a29532718aecfadf5b619851b7f0ab300cf --- /dev/null +++ b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue @@ -0,0 +1,262 @@ +<script setup lang="ts"> +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; +import type { WorkflowVersion } from "@/client/workflow"; +import { WorkflowVersionService } from "@/client/workflow"; +import { onMounted, ref, reactive, computed } from "vue"; +import { Modal, Toast } from "bootstrap"; +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; +import DeleteModal from "@/components/modals/DeleteModal.vue"; + +// Constants +// ============================================================================= +const randomIDSuffix = Math.random().toString(16).substring(2, 8); + +// Bootstrap Elements +// ============================================================================= +let updateIconModal: Modal | null = null; +let successToast: Toast | null = null; + +// Form Elements +// ============================================================================= +const iconUpdateForm = ref<HTMLFormElement | undefined>(undefined); +const iconElement = ref<HTMLImageElement | undefined>(undefined); +const iconInputElement = ref<HTMLInputElement | undefined>(undefined); + +// Props +// ============================================================================= +const props = defineProps<{ + modalID: string; + version: WorkflowVersion; + workflowName?: string; +}>(); + +// Reactive State +// ============================================================================= +const iconUpdate = reactive<{ + icon: Blob | null; +}>({ + icon: null, +}); + +const formState = reactive<{ + loading: boolean; + validated: boolean; + uploadIcon: boolean; +}>({ + loading: false, + validated: false, + uploadIcon: true, +}); + +// Computed Properties +// ============================================================================= +const showIcon = computed<boolean>( + () => props.version.icon_url != undefined || iconUpdate.icon != undefined, +); + +// Events +// ============================================================================= +const emit = defineEmits<{ + (e: "icon-updated", version: WorkflowVersion, icon_url: string): void; + (e: "icon-deleted", version: WorkflowVersion): void; +}>(); + +// Functions +// ============================================================================= +function resetForm() { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + iconElement.value!.src = props.version.icon_url ?? ""; + if (iconInputElement.value != undefined) { + iconInputElement.value.value = ""; + } +} + +function modalClosed() { + formState.validated = false; + resetForm(); +} + +function iconChanged() { + iconUpdate.icon = iconInputElement.value?.files?.[0].slice() ?? null; + if (iconUpdate.icon != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + iconElement.value!.src = URL.createObjectURL(iconUpdate.icon.slice()); + } else { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + iconElement.value!.src = props.version.icon_url ?? ""; + } +} + +function updateIcon() { + formState.validated = true; + if (iconUpdateForm.value?.checkValidity() && iconUpdate.icon != null) { + formState.loading = true; + WorkflowVersionService.workflowVersionUploadWorkflowVersionIcon( + props.version.workflow_id, + props.version.git_commit_hash, + { + icon: iconUpdate.icon, + }, + ) + .then((response) => { + formState.uploadIcon = true; + emit("icon-updated", props.version, response.icon_url); + successToast?.show(); + updateIconModal?.hide(); + }) + .catch((error) => { + console.error(error); + }) + .finally(() => { + formState.loading = false; + }); + } +} + +function deleteIcon() { + formState.loading = true; + WorkflowVersionService.workflowVersionDeleteWorkflowVersionIcon( + props.version.workflow_id, + props.version.git_commit_hash, + ) + .then(() => { + formState.uploadIcon = false; + emit("icon-deleted", props.version); + successToast?.show(); + updateIconModal?.hide(); + }) + .catch((error) => { + console.error(error); + }) + .finally(() => { + formState.loading = false; + }); +} + +// Lifecycle Events +// ============================================================================= +onMounted(() => { + updateIconModal = new Modal("#" + props.modalID); + successToast = new Toast("#successToast-" + randomIDSuffix); +}); +</script> + +<template> + <DeleteModal + v-if="props.version.icon_url" + :modalID="'delete-icon-modal' + randomIDSuffix" + :object-name-delete="'icon for workflow ' + props.workflowName" + :back-modal-id="modalID" + @confirm-delete="deleteIcon()" + /> + <div class="toast-container position-fixed top-toast end-0 p-3"> + <div + role="alert" + aria-live="assertive" + aria-atomic="true" + class="toast text-bg-success align-items-center border-0" + data-bs-autohide="true" + :id="'successToast-' + randomIDSuffix" + > + <div class="d-flex"> + <div v-if="formState.uploadIcon" class="toast-body"> + Successfully uploaded icon + </div> + <div v-else class="toast-body">Successfully deleted icon</div> + <button + type="button" + class="btn-close btn-close-white me-2 m-auto" + data-bs-dismiss="toast" + aria-label="Close" + ></button> + </div> + </div> + </div> + <bootstrap-modal + :modalID="modalID" + :static-backdrop="true" + modal-label="Update Workflow Version IconModal" + v-on="{ 'hidden.bs.modal': modalClosed }" + > + <template v-slot:header> + Update Icon Workflow + <span class="fw-bold" + >{{ props.workflowName }}@{{ props.version.version }}</span + > + </template> + <template v-slot:extra-button v-if="props.version.icon_url"> + <button + class="btn delete-icon" + data-bs-toggle="modal" + :data-bs-target="'#delete-icon-modal' + randomIDSuffix" + > + <font-awesome-icon icon="fa-solid fa-trash" /> + </button> + </template> + <template v-slot:body> + <form + ref="iconUpdateForm" + id="iconUpdateForm" + :class="{ 'was-validated': formState.validated }" + > + <div class="row"> + <div class="col-10"> + <label for="workflowIconInput" class="form-label">New Icon</label> + <input + type="file" + ref="iconInputElement" + accept="image/*" + class="form-control" + id="workflowIconInput" + @change="iconChanged" + aria-describedby="iconHelp" + required + /> + </div> + <div class="col-2"> + <img + :src="props.version.icon_url ?? undefined" + ref="iconElement" + :hidden="!showIcon" + /> + </div> + </div> + </form> + </template> + <template v-slot:footer> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> + Close + </button> + <button + type="submit" + form="iconUpdateForm" + class="btn btn-primary" + :disabled="formState.loading" + @click.prevent="updateIcon" + > + <span + v-if="formState.loading" + class="spinner-border spinner-border-sm" + role="status" + aria-hidden="true" + ></span> + Save + </button> + </template> + </bootstrap-modal> +</template> + +<style scoped> +img { + max-height: 64px; + max-width: 64px; +} + +.delete-icon { + color: var(--bs-secondary) !important; +} + +.delete-icon:hover { + color: var(--bs-danger) !important; +} +</style> diff --git a/src/main.ts b/src/main.ts index ccbaf6cb953600152661909bbee71d6490166e61..a0b5f354ab8ee7e03f16e833b20ae49ace12ea6f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,9 +12,14 @@ import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; // import plugin import isSameOrBefore from "dayjs/plugin/isSameOrBefore"; import "dayjs/locale/en-gb"; +import utc from "dayjs/plugin/utc"; +import timezone from "dayjs/plugin/timezone"; dayjs.extend(relativeTime); // use plugin dayjs.extend(isSameOrBefore); +dayjs.extend(utc); +dayjs.extend(timezone); +dayjs.tz.setDefault(dayjs.tz.guess()); import "bootstrap/dist/css/bootstrap.css"; import "@fortawesome/fontawesome-free/css/fontawesome.css"; diff --git a/src/router/index.ts b/src/router/index.ts index 8abd1c20ea0b19fd0cc8c0381e86aeb4e2e7fcc2..cd6d25646eb4b2ff556af43ba1b667106b4e973a 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -60,6 +60,7 @@ const router = createRouter({ props: (route) => ({ repository: route.query.repository, commit_hash: route.query.commit_hash, + token: route.query.token, }), }, { diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 0022c43a9ff8c467f34ded8d69b94349275d0102..ddb85473ab9af1e3f6a6b547e6012e8396ae2571 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -31,7 +31,7 @@ function parseJwt(token: string): DecodedToken { .map(function (c) { return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); }) - .join("") + .join(""), ); return JSON.parse(jsonPayload) as DecodedToken; @@ -45,7 +45,7 @@ export const useAuthStore = defineStore({ decodedToken: null, user: null, s3key: null, - } as RootState), + }) as RootState, getters: { roles(): string[] { return ( diff --git a/src/stores/buckets.ts b/src/stores/buckets.ts index a884d9f97cfd5a78cc3698679bbc20a702346993..22f91f79fc65734335413af8e72158d5c70ded7f 100644 --- a/src/stores/buckets.ts +++ b/src/stores/buckets.ts @@ -21,12 +21,12 @@ export const useBucketStore = defineStore({ ownPermissions: {}, _lastFetchBucketPromise: undefined, _lastFetchBucketPromiseTimestamp: 0, - } as { + }) as { buckets: BucketOut[]; ownPermissions: Record<string, BucketPermissionOut>; _lastFetchBucketPromise?: CancelablePromise<never>; _lastFetchBucketPromiseTimestamp: number; - }), + }, getters: { ownBucketsAndFullPermissions(): string[] { const authStore = useAuthStore(); @@ -36,7 +36,7 @@ export const useBucketStore = defineStore({ .concat( Object.values(this.ownPermissions) .filter((perm) => perm.permission === Permission.READWRITE) - .map((perm) => perm.bucket_name) + .map((perm) => perm.bucket_name), ); names.sort(); return names; @@ -71,7 +71,7 @@ export const useBucketStore = defineStore({ }); }, getBucketPermission(): ( - bucketName: string + bucketName: string, ) => BucketPermissionOut | undefined { return (bucketName) => this.ownPermissions[bucketName]; }, @@ -120,12 +120,12 @@ export const useBucketStore = defineStore({ | undefined = null, // eslint-disable-next-line @typescript-eslint/no-explicit-any onRejected: ((reason: any) => void) | null | undefined = null, - onFinally: (() => void) | null | undefined = null + onFinally: (() => void) | null | undefined = null, ) { const authStore = useAuthStore(); if (authStore.authenticated && !authStore.foreignUser) { BucketPermissionService.bucketPermissionListPermissionsPerUser( - authStore.currentUID + authStore.currentUID, ) .then((permissions) => { const new_permissions: Record<string, BucketPermissionOut> = {}; @@ -141,7 +141,7 @@ export const useBucketStore = defineStore({ }, deleteOwnPermission(bucketName: string) { this.buckets = this.buckets.filter( - (bucket) => bucket.name !== bucketName + (bucket) => bucket.name !== bucketName, ); delete this.ownPermissions[bucketName]; }, @@ -149,7 +149,7 @@ export const useBucketStore = defineStore({ onFulfilled: ((buckets: BucketOut[]) => void) | null | undefined = null, // eslint-disable-next-line @typescript-eslint/no-explicit-any onRejected: ((reason: any) => void) | null | undefined = null, - onFinally: (() => void) | null | undefined = null + onFinally: (() => void) | null | undefined = null, ) { /* If the time between two calls to this function is less than 5 seconds, * then no API call will be made and the last one reused @@ -179,13 +179,13 @@ export const useBucketStore = defineStore({ onFulfilled: ((bucket: BucketOut) => void) | null | undefined = null, // eslint-disable-next-line @typescript-eslint/no-explicit-any onRejected: ((reason: any) => void) | null | undefined = null, - onFinally: (() => void) | null | undefined = null + onFinally: (() => void) | null | undefined = null, ) { BucketService.bucketGetBucket(bucketName) .then((bucket) => { this.buckets[ this.buckets.findIndex( - (tempBucket) => tempBucket.name === bucket.name + (tempBucket) => tempBucket.name === bucket.name, ) ] = bucket; onFulfilled?.(bucket); @@ -198,12 +198,12 @@ export const useBucketStore = defineStore({ onFulfilled: (() => void) | null | undefined = null, // eslint-disable-next-line @typescript-eslint/no-explicit-any onRejected: ((reason: any) => void) | null | undefined = null, - onFinally: (() => void) | null | undefined = null + onFinally: (() => void) | null | undefined = null, ) { BucketService.bucketDeleteBucket(bucketName, true) .then(() => { this.buckets = this.buckets.filter( - (bucket) => bucket.name !== bucketName + (bucket) => bucket.name !== bucketName, ); onFulfilled?.(); }) @@ -218,13 +218,13 @@ export const useBucketStore = defineStore({ | undefined = null, // eslint-disable-next-line @typescript-eslint/no-explicit-any onRejected: ((reason: any) => void) | null | undefined = null, - onFinally: (() => void) | null | undefined = null + onFinally: (() => void) | null | undefined = null, ) { BucketService.bucketCreateBucket(bucket) .then((createdBucket) => { this.buckets.push(createdBucket); this.buckets.sort((bucketA, bucketB) => - bucketA.name >= bucketB.name ? 1 : -1 + bucketA.name >= bucketB.name ? 1 : -1, ); onFulfilled?.(createdBucket); }) diff --git a/src/utils/DayjsAdapter.ts b/src/utils/DayjsAdapter.ts index 086b6d0b092545f86a362faad8ab98ae6c42d9f2..fbfd8bf6edcce583a130170cb4697b0313275240 100644 --- a/src/utils/DayjsAdapter.ts +++ b/src/utils/DayjsAdapter.ts @@ -78,7 +78,7 @@ _adapters._date.override({ // eslint-disable-next-line @typescript-eslint/no-explicit-any time: any, unit: (TimeUnit & QUnitType) | "isoWeek", - weekday?: number + weekday?: number, ) { if (unit === "isoWeek") { // Ensure that weekday has a valid format diff --git a/src/utils/GitRepository.ts b/src/utils/GitRepository.ts index 697d6963e3d945722115768d35a7fd299ef2c1ea..68004e6c06e19000f3710ff2db33f8430c200301 100644 --- a/src/utils/GitRepository.ts +++ b/src/utils/GitRepository.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import type { AxiosInstance, AxiosBasicCredentials } from "axios"; export const requiredRepositoryFiles = [ "main.nf", @@ -26,13 +27,27 @@ export function determineGitIcon(repo_url?: string): string { export abstract class GitRepository { protected repo: URL; protected gitCommitHash: string; - protected constructor(repoUrl: string, gitCommitHash: string) { + protected token?: string; + protected httpClient: AxiosInstance; + + protected constructor( + repoUrl: string, + gitCommitHash: string, + token?: string, + ) { this.repo = new URL(repoUrl); this.gitCommitHash = gitCommitHash; + if (token != undefined && token.length > 0) { + this.token = token; + } + this.httpClient = axios.create({ + maxRedirects: 2, + }); } + async checkFileExist(filepath: string): Promise<boolean> { try { - await axios.head(this.fileUrl(filepath)); + await this.httpClient.head(this.fileUrl(filepath)); } catch (e) { return false; } @@ -42,7 +57,7 @@ export abstract class GitRepository { // eslint-disable-next-line @typescript-eslint/no-explicit-any async downloadFile(filepath: string): Promise<any> { try { - return await axios.get(this.fileUrl(filepath)); + return await this.httpClient.get(await this.downloadFileUrl(filepath)); } catch (e) { return ""; } @@ -50,10 +65,10 @@ export abstract class GitRepository { async checkFilesExist( files: string[], - raiseError = false + raiseError = false, ): Promise<boolean[]> { const checks = Promise.all( - files.map((filepath) => this.checkFileExist(filepath)) + files.map((filepath) => this.checkFileExist(filepath)), ); const results = await checks; if (raiseError) { @@ -64,16 +79,20 @@ export abstract class GitRepository { } return results; } + protected abstract fileUrl(filepath: string): string; + protected abstract downloadFileUrl(filepath: string): Promise<string>; + static buildRepository( repoUrl: string, - gitCommitHash: string + gitCommitHash: string, + token?: string, ): GitRepository { if (repoUrl.includes("github")) { - return new GithubRepository(repoUrl, gitCommitHash); + return new GithubRepository(repoUrl, gitCommitHash, token); } else if (repoUrl.includes("gitlab")) { - return new GitlabRepository(repoUrl, gitCommitHash); + return new GitlabRepository(repoUrl, gitCommitHash, token); } throw new Error("Repository is not supported."); } @@ -82,31 +101,80 @@ export abstract class GitRepository { class GithubRepository extends GitRepository { private readonly account: string; private readonly repoName: string; - constructor(repoUrl: string, gitCommitHash: string) { - super(repoUrl, gitCommitHash); + + constructor(repoUrl: string, gitCommitHash: string, token?: string) { + super(repoUrl, gitCommitHash, token); const pathParts = this.repo.pathname.slice(1).split("/"); this.account = pathParts[0]; this.repoName = pathParts[1]; + if (token) { + this.httpClient.interceptors.request.use((req) => { + req.auth = { + password: this.token, + username: this.account, + } as AxiosBasicCredentials; + return req; + }); + } + this.httpClient.interceptors.request.use((req) => { + req.headers.setAccept("application/vnd.github.object+json"); + req.headers["X-GitHub-Api-Version"] = "2022-11-28"; + return req; + }); } fileUrl(filepath: string): string { - return `https://raw.githubusercontent.com/${this.account}/${this.repoName}/${this.gitCommitHash}/${filepath}`; + return `https://api.github.com/repos/${this.account}/${ + this.repoName + }/contents/${encodeURIComponent(filepath)}?ref=${encodeURIComponent( + this.gitCommitHash, + )}`; + } + + protected async downloadFileUrl(filepath: string): Promise<string> { + if (this.token != undefined) { + return Promise.resolve( + `https://raw.githubusercontent.com/${this.account}/${this.repoName}/${this.gitCommitHash}/${filepath}`, + ); + } + return (await this.httpClient.get(this.fileUrl(filepath))).data[ + "download_url" + ]; } } class GitlabRepository extends GitRepository { - private readonly account: string[]; - private readonly repoName: string; - constructor(repoUrl: string, gitCommitHash: string) { - super(repoUrl, gitCommitHash); - const pathParts = this.repo.pathname.slice(1).split("/"); - this.account = pathParts.slice(0, pathParts.length - 2); - this.repoName = pathParts[pathParts.length - 1]; + private readonly host: string; + private readonly project: string; + + constructor(repoUrl: string, gitCommitHash: string, token?: string) { + super(repoUrl, gitCommitHash, token); + const url = new URL(repoUrl); + this.host = url.host; + this.project = url.pathname.slice(1); + if (token != undefined) { + this.httpClient.interceptors.request.use((req) => { + req.headers.setAuthorization(`Bearer ${token}`); + return req; + }); + } } fileUrl(filepath: string): string { - return `https://${this.repo.host}/${this.account.reduce((a, b) => - a.concat(`/${b}`) - )}/${this.repoName}/-/raw/${this.gitCommitHash}/${filepath}`; + return `https://${this.host}/api/v4/projects/${encodeURIComponent( + this.project, + )}/repository/files/${encodeURIComponent( + filepath, + )}?ref=${encodeURIComponent(this.gitCommitHash)}`; + } + + protected downloadFileUrl(filepath: string): Promise<string> { + return Promise.resolve( + `https://${this.host}/api/v4/projects/${encodeURIComponent( + this.project, + )}/repository/files/${encodeURIComponent( + filepath, + )}/raw?ref=${encodeURIComponent(this.gitCommitHash)}`, + ); } } diff --git a/src/utils/Workflow.ts b/src/utils/Workflow.ts index c37cab3b551cd8cf4b95597d3304910b83587b4c..9257886a74986eebd6e7bd1da9ed3a3e17bc4fd2 100644 --- a/src/utils/Workflow.ts +++ b/src/utils/Workflow.ts @@ -1,37 +1,27 @@ -import type { - WorkflowVersionReduced, - WorkflowVersionFull, -} from "@/client/workflow"; -import dayjs from "dayjs"; +import type { WorkflowVersion } from "@/client/workflow"; -export function sortedVersions( - versions: WorkflowVersionFull[] -): WorkflowVersionFull[]; -export function sortedVersions( - versions: WorkflowVersionReduced[] -): WorkflowVersionReduced[]; +export function sortedVersions(versions: WorkflowVersion[]): WorkflowVersion[]; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function sortedVersions(versions: any[], desc = true): any[] { +export function sortedVersions( + versions: WorkflowVersion[], + desc = true, +): WorkflowVersion[] { const vs = [...versions]; if (desc) { - vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? 1 : -1)); + vs.sort((a, b) => (a.created_at < b.created_at ? 1 : -1)); } else { - vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? -1 : 1)); + vs.sort((a, b) => (a.created_at < b.created_at ? -1 : 1)); } return vs; } export function latestVersion( - versions: WorkflowVersionFull[] -): WorkflowVersionFull | undefined; + versions: WorkflowVersion[], +): WorkflowVersion | undefined; export function latestVersion( - versions: WorkflowVersionReduced[] -): WorkflowVersionReduced | undefined; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function latestVersion(versions: any[]): any | undefined { + versions: WorkflowVersion[], +): WorkflowVersion | undefined { if (versions == undefined || versions.length == 0) { return undefined; } diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue index 1160460a50734cc7a674fdbc7bcb35fb5f1d8bf7..02296fc30729ddf79389cb55acca6d4504b2fc81 100644 --- a/src/views/object-storage/BucketView.vue +++ b/src/views/object-storage/BucketView.vue @@ -152,7 +152,7 @@ watch( updateObjects(newBucketName); objectState.filterString = ""; } - } + }, ); // Computed Properties @@ -161,17 +161,12 @@ const filteredObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>( () => { return objectState.filterString.length > 0 ? visibleObjects.value.filter((obj) => - obj.key.includes(objectState.filterString) + obj.key.includes(objectState.filterString), ) : visibleObjects.value; - } + }, ); -const bucketNameWithoutTenant = computed<string>(() => { - const splittedBucketName = props.bucketName.split(":"); - return splittedBucketName[splittedBucketName.length - 1]; -}); - const folderStructure = computed<FolderTree>(() => { /** * Store the entire folder structure in a bucket in a tree-like data structure @@ -190,7 +185,7 @@ const folderStructure = computed<FolderTree>(() => { // If the sub folder doesn't exist yet, create it if ( Object.keys(currentFolder.subFolders).find( - (subFolderName) => subFolderName === folderName + (subFolderName) => subFolderName === folderName, ) == undefined ) { currentFolder.subFolders[folderName] = { @@ -210,7 +205,7 @@ const folderStructure = computed<FolderTree>(() => { { subFolders: {}, files: [], - } as FolderTree + } as FolderTree, ); }); @@ -264,10 +259,10 @@ const visibleObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(() => { arr.push( ...Object.keys(currentFolder.subFolders).map((subFolderName) => { const folderSize = calculateFolderSize( - currentFolder.subFolders[subFolderName] + currentFolder.subFolders[subFolderName], ); const folderLastModified = dayjs( - calculateFolderLastModified(currentFolder.subFolders[subFolderName]) + calculateFolderLastModified(currentFolder.subFolders[subFolderName]), ).toISOString(); return { name: subFolderName, @@ -276,22 +271,22 @@ const visibleObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(() => { parentFolder: currentSubFolders.value, last_modified: folderLastModified, } as S3PseudoFolder; - }) + }), ); return arr.filter((obj) => !obj.key.endsWith("/") && obj.key.length > 0); }); const subFolderInUrl = computed<boolean>( - () => currentSubFolders.value.length > 0 + () => currentSubFolders.value.length > 0, ); const errorLoadingObjects = computed<boolean>( - () => objectState.bucketPermissionError || objectState.bucketNotFoundError + () => objectState.bucketPermissionError || objectState.bucketNotFoundError, ); const writableBucket = computed<boolean>(() => - bucketRepository.writableBucket(props.bucketName) + bucketRepository.writableBucket(props.bucketName), ); const readableBucket = computed<boolean>(() => - bucketRepository.readableBucket(props.bucketName) + bucketRepository.readableBucket(props.bucketName), ); // Lifecycle Hooks @@ -301,7 +296,7 @@ onMounted(() => { document .querySelectorAll(".tooltip-container") .forEach( - (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" }) + (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" }), ); successToast = new Toast("#successToast-" + randomIDSuffix); }); @@ -333,11 +328,11 @@ function calculateFolderLastModified(folder: FolderTree): string { .map((f) => dayjs(f.last_modified)) .reduce( (acc, fileAccessed) => (fileAccessed.isAfter(acc) ? fileAccessed : acc), - dayjs("2000-01-01") + dayjs("2000-01-01"), ); for (const subFolderName of Object.keys(folder.subFolders)) { const lastModifiedSubFolder = dayjs( - calculateFolderLastModified(folder.subFolders[subFolderName]) + calculateFolderLastModified(folder.subFolders[subFolderName]), ); if (lastModifiedSubFolder.isAfter(lastModified)) { lastModified = lastModifiedSubFolder; @@ -354,10 +349,7 @@ function updateObjects(bucketName: string) { objectState.bucketNotFoundError = false; objectState.bucketPermissionError = false; objectState.loading = true; - const splittedBucketName = bucketName.split(":"); - ObjectService.objectGetBucketObjects( - splittedBucketName[splittedBucketName.length - 1] - ) + ObjectService.objectGetBucketObjects(bucketName) .then((objs) => { objectState.objects = objs; }) @@ -374,7 +366,7 @@ function updateObjects(bucketName: string) { } function isS3Object( - obj: S3PseudoFolder | S3ObjectWithFolder + obj: S3PseudoFolder | S3ObjectWithFolder, ): obj is S3ObjectWithFolder { return (obj as S3ObjectWithFolder).folder !== undefined; } @@ -386,7 +378,7 @@ function isS3Object( function objectUploaded(newObject: S3ObjectMetaInformation) { bucketRepository.fetchBucket(newObject.bucket); const index = objectState.objects.findIndex( - (obj) => obj.key === newObject.key + (obj) => obj.key === newObject.key, ); if (index > -1) { objectState.objects[index] = newObject; @@ -428,7 +420,7 @@ function confirmedDeleteObject(key: string) { deleteObjectsState.deletedItem = splittedKey[splittedKey.length - 1]; successToast?.show(); objectState.objects = objectState.objects.filter( - (obj) => obj.key !== key + (obj) => obj.key !== key, ); }) .catch((err) => { @@ -484,7 +476,7 @@ function confirmedDeleteFolder(folderPath: string) { deleteObjectsState.deletedItem = splittedPath[splittedPath.length - 2]; successToast?.show(); objectState.objects = objectState.objects.filter( - (obj) => !obj.key.startsWith(folderPath) + (obj) => !obj.key.startsWith(folderPath), ); }) .catch((err) => { @@ -509,7 +501,7 @@ watch( }, 500); } }, - { flush: "post" } + { flush: "post" }, ); </script> @@ -556,9 +548,9 @@ watch( name: 'bucket', params: { bucketName: props.bucketName, subFolders: [] }, }" - >{{ bucketNameWithoutTenant }} + >{{ props.bucketName }} </router-link> - <span v-else class="text-secondary">{{ bucketNameWithoutTenant }}</span> + <span v-else class="text-secondary">{{ props.bucketName }}</span> </li> <li class="breadcrumb-item" @@ -788,7 +780,7 @@ watch( >{{ dayjs(obj.last_modified).fromNow() }}</span > </td> - <td>{{ filesize(obj.size) }}</td> + <td>{{ filesize(obj.size, { base: 2, standard: "jedec" }) }}</td> <!-- Show buttons with dropdown menu if row is an object --> <td class="text-end"> <div @@ -874,7 +866,7 @@ watch( data-bs-target="#delete-object-modal" @click=" deleteFolder( - obj.parentFolder.concat(['']).join('/') + obj.name + '/' + obj.parentFolder.concat(['']).join('/') + obj.name + '/', ) " > diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue index 89d1bc520d36e6576faf48930e0f889aa68ba076..749e75a70929c4970afb275dbde8fc2981159e15 100644 --- a/src/views/object-storage/BucketsView.vue +++ b/src/views/object-storage/BucketsView.vue @@ -35,7 +35,7 @@ function fetchBuckets() { const filteredBuckets = computed<BucketOut[]>(() => { return bucketsState.filterString.length > 0 ? bucketRepository.buckets.filter((bucket) => - bucket.name.includes(bucketsState.filterString) + bucket.name.includes(bucketsState.filterString), ) : bucketRepository.buckets; }); @@ -149,10 +149,11 @@ onMounted(() => { :bucket="{ name: '', description: '', - created_at: '', + created_at: 0, owner: '', size: 0, num_objects: 0, + owner_constraint: null, }" ></bucket-list-item> </div> diff --git a/src/views/object-storage/S3KeyView.vue b/src/views/object-storage/S3KeyView.vue index ed77ed0eb831e1a68372ff0576caacf35ef09059..4366ee644178f1b00e01dd10d6150d046dc3945e 100644 --- a/src/views/object-storage/S3KeyView.vue +++ b/src/views/object-storage/S3KeyView.vue @@ -19,7 +19,7 @@ watch( () => props.s3key.access_key, () => { visibleSecret.value = false; - } + }, ); const visibleSecret = ref<boolean>(false); diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue index 7e8ba0641874715cf89646df536ff3b89d78eb39..eaf3f920a6c8615e0340519cabd49597800c0937 100644 --- a/src/views/object-storage/S3KeysView.vue +++ b/src/views/object-storage/S3KeysView.vue @@ -50,7 +50,7 @@ function deleteKey(accessKey: string) { keyState.deletedKey = accessKey; keyState.activeKey = 0; keyState.keys = keyState.keys.filter( - (s3key) => s3key.access_key !== accessKey + (s3key) => s3key.access_key !== accessKey, ); authStore.setS3Key(keyState.keys[0]); successToast?.show(); @@ -65,7 +65,7 @@ function createKey() { .then((s3key) => { keyState.keys.push(s3key); keyState.keys = [...keyState.keys].sort((keyA, keyB) => - keyA.access_key > keyB.access_key ? 1 : -1 + keyA.access_key > keyB.access_key ? 1 : -1, ); }) .catch((err) => console.error(err)); diff --git a/src/views/workflows/ArbitraryWorkflowView.vue b/src/views/workflows/ArbitraryWorkflowView.vue index 6cbf6d1c2c42c641601ec6dd64cc911081d89b15..cc1c87b0c28732dd69e14989523e1f7c01230f64 100644 --- a/src/views/workflows/ArbitraryWorkflowView.vue +++ b/src/views/workflows/ArbitraryWorkflowView.vue @@ -11,6 +11,7 @@ import { Toast } from "bootstrap"; const props = defineProps<{ repository?: string; commit_hash?: string; + token?: string; }>(); const router = useRouter(); @@ -37,12 +38,13 @@ const workflowExecutionState = reactive<{ const showDocumentation = ref<boolean>(true); let errorToast: Toast | null = null; -function downloadVersionFiles(repository: string, commit_hash: string) { +function downloadVersionFiles( + repository: string, + commit_hash: string, + token?: string, +) { workflowState.loading = true; - const repo = GitRepository.buildRepository(repository, commit_hash); - //const descriptionPromise = repo.downloadFile().then((response) => { - // versionState.descriptionMarkdown = response.data; - //}); + const repo = GitRepository.buildRepository(repository, commit_hash, token); Promise.all( requiredRepositoryFiles.map((file) => repo.downloadFile(file).then((response) => { @@ -57,8 +59,8 @@ function downloadVersionFiles(repository: string, commit_hash: string) { } else if (file.includes("output")) { workflowState.outputMarkdown = response.data; } - }) - ) + }), + ), ).finally(() => { workflowState.loading = false; }); @@ -68,7 +70,7 @@ function startWorkflow( // eslint-disable-next-line @typescript-eslint/no-explicit-any parameters: Record<string, any>, notes?: string, - report_output_bucket?: string + report_output_bucket?: string, ) { if (props.repository && props.commit_hash) { errorToast?.hide(); @@ -78,6 +80,7 @@ function startWorkflow( parameters: parameters, report_output_bucket: report_output_bucket, repository_url: props.repository, + token: props.token, }) .then(() => { router.push({ @@ -100,7 +103,7 @@ function startWorkflow( onMounted(() => { errorToast = new Toast("#arbitraryWorkflowExecutionViewErrorToast"); if (props.commit_hash && props.repository) { - downloadVersionFiles(props.repository, props.commit_hash); + downloadVersionFiles(props.repository, props.commit_hash, props.token); } }); </script> diff --git a/src/views/workflows/ListWorkflowExecutionsView.vue b/src/views/workflows/ListWorkflowExecutionsView.vue index b5068c2719efedd2cbbd2b4ee50b8dd9ae0cdab4..d40942a4e358ed57dda33a82d55f29b21ff761a4 100644 --- a/src/views/workflows/ListWorkflowExecutionsView.vue +++ b/src/views/workflows/ListWorkflowExecutionsView.vue @@ -52,7 +52,7 @@ const sortedExecutions = computed<WorkflowExecutionOut[]>(() => { const tempList = [...executionsState.workflowExecutions]; tempList.sort((a, b) => { // sort by start time descending - return dayjs(a.start_time).isBefore(dayjs(b.start_time)) ? 1 : -1; + return dayjs.unix(a.start_time).isBefore(dayjs.unix(b.start_time)) ? 1 : -1; }); return tempList; }); @@ -64,9 +64,9 @@ const deleteModalString = computed<string>(() => { !executionsState.executionToDelete.workflow_version_id || !executionsState.executionToDelete.workflow_id ) { - return `Workflow Execution from ${dayjs( - executionsState.executionToDelete.start_time - ).format("DD.MM.YYYY HH:mm")}`; + return `Workflow Execution from ${dayjs + .unix(executionsState.executionToDelete.start_time) + .format("DD.MM.YYYY HH:mm")}`; } else { return `Workflow Execution ${ executionsState.workflowMapping[ @@ -76,9 +76,9 @@ const deleteModalString = computed<string>(() => { executionsState.versionMapping[ executionsState.executionToDelete.workflow_version_id ] - } from ${dayjs(executionsState.executionToDelete.start_time).format( - "DD.MM.YYYY HH:mm" - )}`; + } from ${dayjs + .unix(executionsState.executionToDelete.start_time) + .format("DD.MM.YYYY HH:mm")}`; } }); @@ -87,7 +87,7 @@ const deleteModalString = computed<string>(() => { function updateExecutions() { const listExecutionsPromise = WorkflowExecutionService.workflowExecutionListWorkflowExecutions( - userStore.currentUID + userStore.currentUID, ) .then((executions) => { executionsState.workflowExecutions = executions; @@ -104,24 +104,26 @@ function updateExecutions() { .filter((execution) => execution.workflow_id) // filter undefined workflows .filter( (execution) => - !executionsState.workflowMapping[execution.workflow_id] + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !executionsState.workflowMapping[execution.workflow_id!], ) .filter( // filter unique workflows (execution, index, array) => array.findIndex( - (val) => val.workflow_id === execution.workflow_id - ) === index + (val) => val.workflow_id === execution.workflow_id, + ) === index, ) .map((execution) => - WorkflowService.workflowGetWorkflow(execution.workflow_id) - ) - ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + WorkflowService.workflowGetWorkflow(execution.workflow_id!), + ), + ), ) .then((workflows) => workflows.forEach((workflow) => { executionsState.workflowMapping[workflow.workflow_id] = workflow.name; - }) + }), ) .finally(() => { executionsState.mappingLoading = false; @@ -134,34 +136,37 @@ function updateExecutions() { .filter( // filter undefined workflow versions (execution) => - execution.workflow_id && execution.workflow_version_id + execution.workflow_id && execution.workflow_version_id, ) .filter( // filter already seen workflow versions (version) => - !executionsState.versionMapping[version.workflow_version_id] + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !executionsState.versionMapping[version.workflow_version_id!], ) .filter( // filter unique workflow versions (execution, index, array) => array.findIndex( (val) => - val.workflow_version_id === execution.workflow_version_id - ) === index + val.workflow_version_id === execution.workflow_version_id, + ) === index, ) .map((execution) => WorkflowVersionService.workflowVersionGetWorkflowVersion( - execution.workflow_version_id, - execution.workflow_id - ) - ) - ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + execution.workflow_version_id!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + execution.workflow_id!, + ), + ), + ), ) .then((versions) => versions.forEach((version) => { executionsState.versionMapping[version.git_commit_hash] = version.version; - }) + }), ); } @@ -172,7 +177,7 @@ function workflowExecutionDeletable(status: WorkflowExecutionStatus): boolean { WorkflowExecutionStatus.SUCCESS, ].includes(status); } -function workflowExecutionCancable(status: WorkflowExecutionStatus): boolean { +function workflowExecutionCancelable(status: WorkflowExecutionStatus): boolean { return [ WorkflowExecutionStatus.RUNNING, WorkflowExecutionStatus.PENDING, @@ -183,11 +188,11 @@ function workflowExecutionCancable(status: WorkflowExecutionStatus): boolean { function deleteWorkflowExecution(executionId?: string) { if (executionId) { WorkflowExecutionService.workflowExecutionDeleteWorkflowExecution( - executionId + executionId, ).then(() => { executionsState.workflowExecutions = executionsState.workflowExecutions.filter( - (execution) => execution.execution_id !== executionId + (execution) => execution.execution_id !== executionId, ); }); } @@ -195,16 +200,15 @@ function deleteWorkflowExecution(executionId?: string) { function cancelWorkflowExecution(executionId: string) { WorkflowExecutionService.workflowExecutionCancelWorkflowExecution( - executionId + executionId, ).then(() => { const index = executionsState.workflowExecutions.findIndex( - (execution) => execution.execution_id === executionId + (execution) => execution.execution_id === executionId, ); if (index > -1) { executionsState.workflowExecutions[index].status = WorkflowExecutionStatus.CANCELED; - executionsState.workflowExecutions[index].end_time = - dayjs().toISOString(); + executionsState.workflowExecutions[index].end_time = dayjs().unix(); } }); } @@ -300,7 +304,7 @@ onMounted(() => { </td> <td v-else> <router-link - v-if="execution.workflow_id" + v-if="execution.workflow_id && execution.workflow_version_id" :to="{ name: 'workflow-version', params: { @@ -324,10 +328,12 @@ onMounted(() => { />{{ execution.status }}</span > </td> - <td>{{ dayjs(execution.start_time).format("DD.MM.YYYY HH:mm") }}</td> + <td> + {{ dayjs.unix(execution.start_time).format("DD.MM.YYYY HH:mm") }} + </td> <td> <template v-if="execution.end_time"> - {{ dayjs(execution.end_time).format("DD.MM.YYYY HH:mm") }} + {{ dayjs.unix(execution.end_time).format("DD.MM.YYYY HH:mm") }} </template> <template v-else> - </template> </td> @@ -347,7 +353,7 @@ onMounted(() => { <span class="visually-hidden">Toggle Dropdown</span> </button> <ul class="dropdown-menu dropdown-menu"> - <li v-if="workflowExecutionCancable(execution.status)"> + <li v-if="workflowExecutionCancelable(execution.status)"> <button class="dropdown-item text-danger align-middle" type="button" diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue index 66d7f92a535e280ef044d2f9375841d63ffbbba9..2cbb2e0569173e5359991315414aa403be8fbc0a 100644 --- a/src/views/workflows/ListWorkflowsView.vue +++ b/src/views/workflows/ListWorkflowsView.vue @@ -30,8 +30,8 @@ const filterFunctionMapping: Record< name: (a: WorkflowOut, b: WorkflowOut) => workflowsState.sortDesc ? a.name > b.name : a.name < b.name, release: (a: WorkflowOut, b: WorkflowOut) => { - const a_date = dayjs(a.versions[a.versions.length - 1].created_at); - const b_date = dayjs(b.versions[b.versions.length - 1].created_at); + const a_date = dayjs.unix(a.versions[a.versions.length - 1].created_at); + const b_date = dayjs.unix(b.versions[b.versions.length - 1].created_at); return workflowsState.sortDesc ? a_date.isBefore(b_date) : a_date.isAfter(b_date); @@ -53,10 +53,10 @@ const processedWorkflows = computed<WorkflowOut[]>(() => { ...workflowsState.workflows.filter( (workflow) => filterWorkflowByString(workflow) && - filterWorkflowWithoutVersion(workflow) + filterWorkflowWithoutVersion(workflow), ), ].sort((a, b) => - filterFunctionMapping[workflowsState.sortByAttribute](a, b) ? 1 : -1 + filterFunctionMapping[workflowsState.sortByAttribute](a, b) ? 1 : -1, ); }); @@ -204,6 +204,7 @@ onMounted(() => { workflow_id: '', versions: [], developer_id: '', + private: false, }" loading /> diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue index f9507273f1b97194524127a0d9edb96a43de8312..882df7869b1feb2b2c6338b46e4f8a0604d3a6de 100644 --- a/src/views/workflows/MyWorkflowsView.vue +++ b/src/views/workflows/MyWorkflowsView.vue @@ -1,6 +1,6 @@ <script setup lang="ts"> import { onMounted, reactive } from "vue"; -import type { WorkflowOut, WorkflowVersionFull } from "@/client/workflow"; +import type { WorkflowOut, WorkflowVersion } from "@/client/workflow"; import { Status, WorkflowService } from "@/client/workflow"; import { useAuthStore } from "@/stores/auth"; import WorkflowWithVersionsCard from "@/components/workflows/WorkflowWithVersionsCard.vue"; @@ -8,6 +8,8 @@ import CreateWorkflowModal from "@/components/workflows/modals/CreateWorkflowMod import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue"; import UpdateWorkflowModal from "@/components/workflows/modals/UpdateWorkflowModal.vue"; import DeleteModal from "@/components/modals/DeleteModal.vue"; +import UpdateWorkflowVersionIconModal from "@/components/workflows/modals/UpdateWorkflowVersionIconModal.vue"; +import UpdateWorkflowCredentialsModal from "@/components/workflows/modals/UpdateWorkflowCredentialsModal.vue"; const userRepository = useAuthStore(); const workflowsState = reactive<{ @@ -15,6 +17,7 @@ const workflowsState = reactive<{ loading: boolean; updateWorkflow: WorkflowOut; potentialWorkflowDelete?: WorkflowOut; + updateIconVersion: WorkflowVersion; }>({ workflows: [], loading: true, @@ -25,14 +28,27 @@ const workflowsState = reactive<{ versions: [ { version: "1.0.0", - created_at: "01.01.2023", + created_at: 0, git_commit_hash: "", status: Status.CREATED, + workflow_id: "", + icon_url: "", + modes: [], }, ], repository_url: "", workflow_id: "", developer_id: "", + private: false, + }, + updateIconVersion: { + version: "", + workflow_id: "", + git_commit_hash: "", + modes: null, + icon_url: null, + created_at: 0, + status: Status.CREATED, }, }); @@ -40,16 +56,14 @@ function workflowUpdateClicked(workflow: WorkflowOut) { workflowsState.updateWorkflow = workflow; } -function workflowUpdated(version: WorkflowVersionFull) { +function iconUpdateClicked(version: WorkflowVersion) { + workflowsState.updateIconVersion = version; +} + +function workflowUpdated(version: WorkflowVersion) { workflowsState.workflows .find((w) => w.workflow_id == version.workflow_id) - ?.versions.push({ - status: version.status, - git_commit_hash: version.git_commit_hash, - icon_url: version.icon_url, - created_at: version.created_at, - version: version.version, - }); + ?.versions.push(version); } function workflowDeleteClicked(workflow: WorkflowOut) { @@ -61,7 +75,7 @@ function confirmedWorkflowDelete(workflow_id?: string) { WorkflowService.workflowDeleteWorkflow(workflow_id) .then(() => { workflowsState.workflows = workflowsState.workflows.filter( - (workflow) => workflow.workflow_id !== workflow_id + (workflow) => workflow.workflow_id !== workflow_id, ); }) .finally(() => { @@ -70,11 +84,47 @@ function confirmedWorkflowDelete(workflow_id?: string) { } } +function workflowCredentialsUpdated( + workflow: WorkflowOut, + credentialsDeleted: boolean, +) { + const index = workflowsState.workflows.findIndex( + (w) => w.workflow_id == workflow.workflow_id, + ); + if (index > -1) { + workflowsState.workflows[index].private = !credentialsDeleted; + } +} + +function confirmedWorkflowVersionIconUpdate( + version: WorkflowVersion, + icon_url?: string, +) { + const wIndex = workflowsState.workflows.findIndex( + (w) => w.workflow_id == version.workflow_id, + ); + if (wIndex > -1) { + const vIndex = workflowsState.workflows[wIndex].versions.findIndex( + (v) => v.git_commit_hash == version.git_commit_hash, + ); + if (vIndex > -1) { + setTimeout(() => { + workflowsState.workflows[wIndex].versions[vIndex].icon_url = + icon_url ?? null; + }, 1000); + } + } +} + +function updateIconClicked(version: WorkflowVersion) { + workflowsState.updateIconVersion = version; +} + onMounted(() => { WorkflowService.workflowListWorkflows( undefined, Object.values(Status), - userRepository.currentUID + userRepository.currentUID, ) .then((workflows) => { workflowsState.workflows = workflows; @@ -100,10 +150,26 @@ onMounted(() => { :object-name-delete="workflowsState.potentialWorkflowDelete?.name" @confirm-delete=" confirmedWorkflowDelete( - workflowsState.potentialWorkflowDelete?.workflow_id + workflowsState.potentialWorkflowDelete?.workflow_id, ) " /> + <update-workflow-version-icon-modal + modal-i-d="updateWorkflowVersionIconModal" + :workflow-name=" + workflowsState.workflows.find( + (w) => w.workflow_id == workflowsState.updateIconVersion.workflow_id, + )?.name + " + :version="workflowsState.updateIconVersion" + @icon-updated="confirmedWorkflowVersionIconUpdate" + @icon-deleted="confirmedWorkflowVersionIconUpdate" + /> + <update-workflow-credentials-modal + :workflow="workflowsState.updateWorkflow" + modal-i-d="updateWorkflowCredentialsModal" + @credentials-updated="workflowCredentialsUpdated" + /> <div class="row m-2 border-bottom mb-4 justify-content-between align-items-center pb-2" > @@ -127,7 +193,9 @@ onMounted(() => { :workflow="workflow" :loading="false" @workflow-update-click="workflowUpdateClicked" + @workflow-update-credentials-click="workflowUpdateClicked" @workflow-delete-click="workflowDeleteClicked" + @workflow-update-icon-click="iconUpdateClicked" /> </card-transition-group> <div v-else class="text-center mt-5 fs-2"> @@ -151,8 +219,10 @@ onMounted(() => { repository_url: '', short_description: '', developer_id: '', + private: false, }" loading + @workflow-update-icon-click="updateIconClicked" /> </div> </template> diff --git a/src/views/workflows/ReviewWorkflowsView.vue b/src/views/workflows/ReviewWorkflowsView.vue index c752ad89036e6648a8fe436148ad9e65dd8144c3..fd121ea16ac775d9c0c96742781fe7cac805bf46 100644 --- a/src/views/workflows/ReviewWorkflowsView.vue +++ b/src/views/workflows/ReviewWorkflowsView.vue @@ -28,23 +28,23 @@ const workflowsState = reactive<{ function updateWorkflowVersionStatus( workflow_id: string, git_commit_hash: string, - new_status: Status + new_status: Status, ) { workflowsState.versionUpdateLoading = git_commit_hash; WorkflowVersionService.workflowVersionUpdateWorkflowVersionStatus( git_commit_hash, workflow_id, - { status: new_status } as WorkflowVersionStatus + { status: new_status } as WorkflowVersionStatus, ) .then(() => { const workflowIndex = workflowsState.workflows.findIndex( - (workflow) => workflow.workflow_id === workflow_id + (workflow) => workflow.workflow_id === workflow_id, ); if (workflowIndex >= 0) { const versionIndex = workflowsState.workflows[ workflowIndex ].versions.findIndex( - (version) => version.git_commit_hash === git_commit_hash + (version) => version.git_commit_hash === git_commit_hash, ); if (versionIndex >= 0) { workflowsState.workflows[workflowIndex].versions[ @@ -64,8 +64,8 @@ function fetchDevelopers(workflows: WorkflowOut[]) { workflows.map((workflow) => UserService.userGetUser(workflow.developer_id).then((developer) => { workflowsState.developers[developer.uid] = developer.display_name; - }) - ) + }), + ), ).finally(() => { workflowsState.developersLoading = false; }); @@ -210,7 +210,7 @@ onMounted(() => { updateWorkflowVersionStatus( workflow.workflow_id, version.git_commit_hash, - Status.PUBLISHED + Status.PUBLISHED, ) " > @@ -223,7 +223,7 @@ onMounted(() => { updateWorkflowVersionStatus( workflow.workflow_id, version.git_commit_hash, - Status.DENIED + Status.DENIED, ) " > diff --git a/src/views/workflows/StartWorkflowView.vue b/src/views/workflows/StartWorkflowView.vue index f0f9d1f2d1cea3dc245e4ce81c1556e6d8200772..6a6dba2545d4b2bbb27f325e2c5d124932f2ec46 100644 --- a/src/views/workflows/StartWorkflowView.vue +++ b/src/views/workflows/StartWorkflowView.vue @@ -1,12 +1,12 @@ <script setup lang="ts"> import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue"; -import type { WorkflowVersionFull } from "@/client/workflow"; +import type { WorkflowVersion } from "@/client/workflow"; import { ApiError, + DocumentationEnum, WorkflowExecutionService, WorkflowVersionService, } from "@/client/workflow"; -import axios from "axios"; import { onMounted, ref, reactive } from "vue"; import { useRouter } from "vue-router"; import { Toast } from "bootstrap"; @@ -23,7 +23,7 @@ let errorToast: Toast | null = null; const versionState = reactive<{ // eslint-disable-next-line @typescript-eslint/no-explicit-any parameterSchema?: Record<string, any>; - workflowVersion?: WorkflowVersionFull; + workflowVersion?: WorkflowVersion; loading: boolean; workflowExecutionError?: string; }>({ @@ -36,7 +36,7 @@ const versionState = reactive<{ function downloadVersion() { WorkflowVersionService.workflowVersionGetWorkflowVersion( props.versionId, - props.workflowId + props.workflowId, ) .then((version) => { versionState.workflowVersion = version; @@ -45,9 +45,13 @@ function downloadVersion() { .then(downloadVersionFiles); } -function downloadVersionFiles(version: WorkflowVersionFull) { - axios.get(version.parameter_schema_url).then((response) => { - parameterSchema.value = response.data; +function downloadVersionFiles(version: WorkflowVersion) { + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + version.workflow_id, + version.git_commit_hash, + DocumentationEnum.PARAMETER_SCHEMA, + ).then((markdown) => { + parameterSchema.value = markdown; }); } @@ -55,7 +59,7 @@ function startWorkflow( // eslint-disable-next-line @typescript-eslint/no-explicit-any parameters: Record<string, any>, notes?: string, - report_output_bucket?: string + report_output_bucket?: string, ) { if (props.versionId) { versionState.workflowExecutionError = undefined; diff --git a/src/views/workflows/WorkflowVersionView.vue b/src/views/workflows/WorkflowVersionView.vue index 4b070c2e1fd65ff9952268f0d36076e5624c9d3b..42de6e1fddad6a8e27da3cfbb69136abbc90b946 100644 --- a/src/views/workflows/WorkflowVersionView.vue +++ b/src/views/workflows/WorkflowVersionView.vue @@ -1,8 +1,7 @@ <script setup lang="ts"> import { onMounted, reactive, watch } from "vue"; -import { WorkflowVersionService } from "@/client/workflow"; -import type { WorkflowVersionFull } from "@/client/workflow"; -import axios from "axios"; +import type { WorkflowVersion } from "@/client/workflow"; +import { DocumentationEnum, WorkflowVersionService } from "@/client/workflow"; import WorkflowDocumentationTabs from "@/components/workflows/WorkflowDocumentationTabs.vue"; const props = defineProps<{ @@ -13,7 +12,7 @@ const props = defineProps<{ const versionState = reactive<{ loading: boolean; fileLoading: boolean; - version: undefined | WorkflowVersionFull; + version: undefined | WorkflowVersion; descriptionMarkdown: string; changelogMarkdown: string; usageMarkdown: string; @@ -39,7 +38,7 @@ watch( // If bucket is changed, update the objects updateVersion(newVersionId, props.workflowId); } - } + }, ); function updateVersion(versionId: string, workflowId: string) { @@ -47,7 +46,7 @@ function updateVersion(versionId: string, workflowId: string) { versionState.fileLoading = true; WorkflowVersionService.workflowVersionGetWorkflowVersion( versionId, - workflowId + workflowId, ) .then((version) => { versionState.version = version; @@ -62,25 +61,48 @@ function updateVersion(versionId: string, workflowId: string) { }); } -function downloadVersionFiles(version: WorkflowVersionFull) { +function downloadVersionFiles(version: WorkflowVersion) { versionState.fileLoading = true; - const descriptionPromise = axios.get(version.readme_url).then((response) => { - versionState.descriptionMarkdown = response.data; - }); - const changelogPromise = axios.get(version.changelog_url).then((response) => { - versionState.changelogMarkdown = response.data; - }); - const parameterPromise = axios - .get(version.parameter_schema_url) - .then((response) => { - versionState.parameterSchema = response.data; + const descriptionPromise = + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + version.workflow_id, + version.git_commit_hash, + DocumentationEnum.USAGE, + ).then((markdown) => { + versionState.descriptionMarkdown = markdown; + }); + const changelogPromise = + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + version.workflow_id, + version.git_commit_hash, + DocumentationEnum.CHANGELOG, + ).then((markdown) => { + versionState.changelogMarkdown = markdown; + }); + const parameterPromise = + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + version.workflow_id, + version.git_commit_hash, + DocumentationEnum.PARAMETER_SCHEMA, + ).then((markdown) => { + versionState.parameterSchema = markdown; + }); + const usagePromise = + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + version.workflow_id, + version.git_commit_hash, + DocumentationEnum.INPUT, + ).then((markdown) => { + versionState.usageMarkdown = markdown; + }); + const outputPromise = + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + version.workflow_id, + version.git_commit_hash, + DocumentationEnum.OUTPUT, + ).then((markdown) => { + versionState.outputMarkdown = markdown; }); - const usagePromise = axios.get(version.usage_url).then((response) => { - versionState.usageMarkdown = response.data; - }); - const outputPromise = axios.get(version.output_url).then((response) => { - versionState.outputMarkdown = response.data; - }); Promise.all([ descriptionPromise, changelogPromise, diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue index e623dcfaf2295ce0d18a37b6633a8c05733cf51c..05e3ee2cfe6507f6fc40e63604ab74e8071ba375 100644 --- a/src/views/workflows/WorkflowView.vue +++ b/src/views/workflows/WorkflowView.vue @@ -3,7 +3,7 @@ import { computed, onMounted, reactive, watch } from "vue"; import type { WorkflowOut, WorkflowStatistic, - WorkflowVersionReduced, + WorkflowVersion, } from "@/client/workflow"; import WorkflowStatisticsChart from "@/components/workflows/WorkflowStatisticsChart.vue"; import { @@ -58,14 +58,14 @@ watch( if (newWorkflowId !== oldWorkflowId) { updateWorkflow(newWorkflowId); } - } + }, ); watch( () => props.versionId, (newWorkflowId) => { workflowState.activeVersionId = newWorkflowId ?? ""; - } + }, ); watch( @@ -82,38 +82,39 @@ watch( query: { tab: route.query.tab }, }); } - } + }, ); // Computed Properties // ============================================================================= -const latestVersion = computed<WorkflowVersionReduced | undefined>(() => +const latestVersion = computed<WorkflowVersion | undefined>(() => calculateLatestVersion( workflowState.workflow?.versions?.filter( - (version) => version.status == Status.PUBLISHED - ) || [] - ) + (version) => version.status == Status.PUBLISHED, + ) || [], + ), ); -const activeVersion = computed<WorkflowVersionReduced | undefined>(() => - workflowState.workflow?.versions.find( - (w) => w.git_commit_hash === workflowState.activeVersionId - ) +const activeVersion = computed<WorkflowVersion | undefined>( + () => + workflowState.workflow?.versions.find( + (w) => w.git_commit_hash === workflowState.activeVersionId, + ), ); const activeVersionString = computed<string>( - () => activeVersion.value?.version ?? "" + () => activeVersion.value?.version ?? "", ); const activeVersionIcon = computed<string | undefined>( - () => activeVersion.value?.icon_url + () => activeVersion.value?.icon_url ?? undefined, ); const versionLaunchable = computed<boolean>( - () => activeVersion.value?.status == Status.PUBLISHED ?? false + () => activeVersion.value?.status == Status.PUBLISHED ?? false, ); const gitIcon = computed<string>(() => - determineGitIcon(workflowState.workflow?.repository_url) + determineGitIcon(workflowState.workflow?.repository_url), ); const allowVersionDeprecation = computed<boolean>(() => { @@ -160,11 +161,11 @@ function deprecateCurrentWorkflowVersion() { if (props.versionId) { WorkflowVersionService.workflowVersionDeprecateWorkflowVersion( props.versionId, - props.workflowId + props.workflowId, ).then((version) => { if (workflowState.workflow) { const versionIndex = workflowState.workflow.versions.findIndex( - (v) => v.git_commit_hash === version.git_commit_hash + (v) => v.git_commit_hash === version.git_commit_hash, ); if (versionIndex > -1) { workflowState.workflow.versions[versionIndex].status = version.status; @@ -206,13 +207,12 @@ onMounted(() => { {{ workflowState.workflow.name }} <span v-if="activeVersionString">@{{ activeVersionString }}</span> </div> - <a :href="workflowState.workflow.repository_url" target="_blank"> - <img - v-if="activeVersionIcon != null" - :src="activeVersionIcon" - class="img-fluid icon" - alt="Workflow icon" - /></a> + <img + v-if="activeVersionIcon != null" + :src="activeVersionIcon" + class="img-fluid icon" + alt="Workflow icon" + /> </div> <p class="fs-4 mb-5 mt-3">{{ workflowState.workflow.short_description }}</p> <template v-if="route.name !== 'workflow-start'"> @@ -232,8 +232,8 @@ onMounted(() => { }, query: { tab: route.query.tab }, }" - >Try the latest version {{ latestVersion.version }}.</router-link - > + >Try the latest version {{ latestVersion.version }}. + </router-link> </div> <div class="row align-items-center"> <div class="w-fit position-absolute start-0"> @@ -293,8 +293,13 @@ onMounted(() => { <font-awesome-icon :icon="gitIcon" class="me-1" /> <span class="align-middle"> {{ workflowState.workflow.repository_url }}</span - ></a - > + > + <font-awesome-icon + v-if="workflowState.workflow?.private" + icon="fa-solid fa-lock" + class="ms-1" + /> + </a> </div> <workflow-statistics-chart :stats="workflowState.stats" @@ -317,13 +322,9 @@ onMounted(() => { </template> <style scoped> -.icon:hover { - opacity: 0.8; -} - .icon { - max-width: 60px; - max-height: 60px; + max-width: 64px; + max-height: 64px; min-width: 50px; min-height: 50px; } diff --git a/tsconfig.json b/tsconfig.json index 0f216f1416aff7a546a0218b93976f2282c8460f..32d333fdaaf1d55c8b026efa8c95a87163d171f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,16 @@ { - "extends": "@vue/tsconfig/tsconfig.web.json", + "extends": [ + "@tsconfig/node18/tsconfig.json", + "@vue/tsconfig/tsconfig.json" + ], "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], "compilerOptions": { "lib": ["...", "dom"], "allowSyntheticDefaultImports": true, "baseUrl": ".", + "types": ["node"], "paths": { "@/*": ["./src/*"] } }, - - "references": [ - { - "path": "./tsconfig.config.json" - } - ] }